diff --git a/.github/workflows/format-check-and-test.yml b/.github/workflows/format-check-and-test.yml
new file mode 100644
index 000000000..ae8dcf103
--- /dev/null
+++ b/.github/workflows/format-check-and-test.yml
@@ -0,0 +1,40 @@
+# https://arkmowers.github.io/arknights-mower/dev/environment.html#auto-formatting
+
+name: 代码格式检查与测试
+on:
+ - push
+ - pull_request
+
+jobs:
+ ruff:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: chartboost/ruff-action@v1
+ - uses: chartboost/ruff-action@v1
+ with:
+ args: "format --check"
+
+ prettier:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: creyD/prettier_action@v4.3
+ with:
+ dry: True
+ prettier_options: "--check ui/**/*.js ui/**/*.vue"
+
+ unittest:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.12
+ cache: pip
+ cache-dependency-path: requirements.txt
+ - run: |
+ python3.12 -m venv venv
+ ./venv/bin/pip install -r requirements.txt
+ - run: |
+ ./venv/bin/python3 -m unittest discover -s arknights_mower/tests -p "*_tests.py"
diff --git a/.github/workflows/pyinstaller-win-shawn.yml b/.github/workflows/pyinstaller-win-shawn.yml
new file mode 100644
index 000000000..934785409
--- /dev/null
+++ b/.github/workflows/pyinstaller-win-shawn.yml
@@ -0,0 +1,40 @@
+# https://arkmowers.github.io/arknights-mower/dev/environment.html#auto-formatting
+
+name: 代码格式检查与测试
+on:
+ - push
+ - pull_request
+
+jobs:
+ ruff:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: chartboost/ruff-action@v1
+ - uses: chartboost/ruff-action@v1
+ with:
+ args: "format --check"
+
+ prettier:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: creyD/prettier_action@v4.3
+ with:
+ dry: True
+ prettier_options: "--check ui/**/*.js ui/**/*.vue"
+
+ unittest:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: 3.12
+ cache: pip
+ cache-dependency-path: requirements.txt
+ - run: |
+ python3.12 -m venv venv
+ ./venv/bin/pip install -r requirements.txt
+ - run: |
+ ./venv/bin/python3 -m unittest discover -s arknights_mower/tests -p "*_tests.py"
\ No newline at end of file
diff --git a/.github/workflows/pyinstaller-win.yml b/.github/workflows/pyinstaller-win.yml
deleted file mode 100644
index d6785984a..000000000
--- a/.github/workflows/pyinstaller-win.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: Windows Binary Package
-
-on:
- push:
- tags:
- - 'v[0-9]+.[0-9]+.[0-9]+'
-
-jobs:
- build-win-amd64:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v2
- - name: Get the version
- id: get_version
- shell: bash
- run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
- - name: Set up Python 3.9 amd64
- uses: actions/setup-python@v2
- with:
- python-version: 3.9
- architecture: x64
- - name: Install dependencies
- shell: cmd
- run: |
- python -m pip install --upgrade pip
- python -m venv venv64
- venv64\Scripts\python -m pip install --upgrade pip wheel setuptools
- venv64\Scripts\python -m pip install -r requirements.txt
- venv64\Scripts\python -m pip install pyinstaller==5.4.1
- - name: Make package
- shell: cmd
- run: |
- venv64\Scripts\pyinstaller .\main.spec
- md public
- move dist\main.exe public\arknights_mower.exe
- - name: Package into zip
- uses: vimtor/action-zip@v1
- with:
- files: public/
- recursive: false
- dest: arknights-mower_cp39_win_amd64_${{ steps.get_version.outputs.VERSION }}.zip
- - name: Release
- uses: softprops/action-gh-release@v1
- with:
- draft: true
- body_path: doc/CHANGELOG.md
- files: arknights-mower_cp39_win_amd64_${{ steps.get_version.outputs.VERSION }}.zip
diff --git a/.gitignore b/.gitignore
index cbaaa07e0..9d4fde1fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+FZDYSK.TTF
+
!main.spec
*.yaml
!arknights_mower/templates/*.yaml
@@ -28,11 +30,15 @@ __pycache__/
*.py[cod]
*$py.class
+# Nodejs Version Manager
+.nvmrc
+
# C extensions
*.so
# Distribution / packaging
.Python
+mower/
build/
develop-eggs/
dist/
@@ -56,7 +62,6 @@ MANIFEST
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
-*.spec
# Installer logs
pip-log.txt
@@ -109,6 +114,13 @@ ipython_config.py
# pyenv
.python-version
+# nodenv
+.node-version
+
+# syncthing
+.stfolder/
+.stignore
+
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
@@ -175,4 +187,26 @@ dmypy.json
###JetBrains Product###
.idea/*
+### 命令行运行产生的/tmp/data.db
+/tmp/
+
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
+/*.json
+dist
+MAA
+/*.yml
+
+# venv
+/bin/
+/pyvenv.cfg
+/lib64
+
+#testspace
+/testspace
+
+
+/.buildozer/
+
+
+*.ipynb
+.rgignore
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..314906e0f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "ArknightsGameResource"]
+ path = ArknightsGameResource
+ url = https://github.com/yuanyan3060/ArknightsGameResource
diff --git a/ArknightsGameResource b/ArknightsGameResource
new file mode 160000
index 000000000..9826fca7f
--- /dev/null
+++ b/ArknightsGameResource
@@ -0,0 +1 @@
+Subproject commit 9826fca7f304b5f3e39bd42f4406a27b1015bf2c
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..3761c0882
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,47 @@
+FROM ubuntu:22.04 AS ubuntu-base
+
+# 设置环境变量以避免tzdata的交互式提示
+ENV DEBIAN_FRONTEND noninteractive
+
+# 安装必要的系统库
+RUN apt-get update && \
+ apt-get install -y software-properties-common && \
+ add-apt-repository ppa:deadsnakes/ppa && \
+ apt-get update && \
+ apt-get install -y python3.8 python3.8-venv python3.8-tk python3.8-dev python3-pip \
+ build-essential libgirepository1.0-dev gcc libcairo2-dev pkg-config libzbar0 adb git \
+ libgtk-3-dev gir1.2-webkit2-4.1 gir1.2-appindicator3-0.1 gobject-introspection tk8.6 \
+ xvfb \
+ dbus \
+ && rm -rf /var/lib/apt/lists/*
+
+
+
+# 使用官方Node.js 18镜像作为基础镜像构建前端
+FROM node:18 AS node-base
+WORKDIR /app/ui
+
+# 安装前端依赖并构建
+COPY ui/package*.json ./
+COPY ui/. .
+RUN npm ci
+RUN npm run build --no-update-notifier
+
+
+
+# 合并阶段,使用Python环境为基础,将构建好的前端加入
+FROM ubuntu-base AS final
+WORKDIR /app
+COPY --from=node-base /app/ui/dist ./ui/dist
+COPY . .
+
+
+# 设置Python虚拟环境并安装依赖
+RUN python3.8 -m venv venv
+
+RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
+
+RUN . venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt && pip install pycairo PyGObject
+
+# 运行应用
+ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"]
\ No newline at end of file
diff --git a/FontPruner/SourceHanSansSC-Bold.ttf b/FontPruner/SourceHanSansSC-Bold.ttf
deleted file mode 100644
index eeb52959f..000000000
Binary files a/FontPruner/SourceHanSansSC-Bold.ttf and /dev/null differ
diff --git a/FontPruner/sfnttool.jar b/FontPruner/sfnttool.jar
deleted file mode 100644
index 6cf8ad30d..000000000
Binary files a/FontPruner/sfnttool.jar and /dev/null differ
diff --git a/Mower.py b/Mower.py
new file mode 100644
index 000000000..cdc3d7d46
--- /dev/null
+++ b/Mower.py
@@ -0,0 +1,395 @@
+import atexit
+import json
+import os
+import time
+from datetime import datetime
+
+from arknights_mower.solvers.base_schedule import BaseSchedulerSolver
+from arknights_mower.utils import config, rapidocr
+from arknights_mower.utils.email import task_template
+from arknights_mower.utils.log import init_fhlr, logger
+from arknights_mower.utils.plan import Plan, PlanConfig, Room
+from arknights_mower.utils.simulator import restart_simulator
+
+# 下面不能删除
+
+send_message_config = {
+ # QQ邮箱通知配置
+ "email_config": {
+ # 发信账户
+ "account": "xxx@qq.com",
+ # 在QQ邮箱“帐户设置-账户-开启SMTP服务”中,按照指示开启服务获得授权码
+ "pass_code": "xxx",
+ # 收件人邮箱
+ "recipients": ["任何邮箱"],
+ # 是否启用邮件提醒
+ "mail_enable": False,
+ # 邮件主题
+ "subject": "任务数据",
+ "custom_smtp_server": {"enable": False},
+ },
+ # Server酱通知配置
+ "serverJang_push_config": {
+ # 是否启用Server酱提示
+ "server_push_enable": False,
+ # Key值
+ "sendKey": "xxx",
+ },
+}
+maa_config = {
+ "maa_enable": True,
+ # 请设置为存放 dll 文件及资源的路径
+ "maa_path": "F:\\MAA-v4.20.0-win-x64",
+ # 请设置为存放 dll 文件及资源的路径
+ "maa_adb_path": "F:\\MAA-v4.20.0-win-x64\\adb\\platform-tools\\adb.exe",
+ # adb 地址
+ "maa_adb": ["127.0.0.1:16384"],
+ # maa 运行的时间间隔,以小时计
+ "maa_execution_gap": 4,
+ # 以下配置,第一个设置为true的首先生效
+ # 是否启动肉鸽
+ "roguelike": False,
+ # 是否启动生息演算
+ "reclamation_algorithm": False,
+ # 是否启动保全派驻
+ "stationary_security_service": False,
+ # 保全派驻类别 1-2
+ "sss_type": 2,
+ # 导能单元类别 1-3
+ "ec_type": 1,
+ "copilot_file_location": "C:\\Users\\frank\\Desktop\\MAACopilot_保全派驻-阿尔斯特甜品制作平台-澄闪铃兰双核.json",
+ "copilot_loop_times": 10,
+ "last_execution": None,
+ "blacklist": "家具,碳,加急许可",
+ "rogue_theme": "Sami",
+ "buy_first": "招聘许可",
+ "recruitment_permit": 30,
+ "credit_fight": True,
+ "recruitment_time": None,
+ "mall_ignore_when_full": True,
+ "touch_option": "maatouch",
+ "conn_preset": "General",
+ "rogue": {
+ "squad": "指挥分队",
+ "roles": "取长补短",
+ "use_support": False,
+ "core_char": "",
+ "use_nonfriend_support": False,
+ "mode": 0,
+ "investment_enabled": True,
+ "stop_when_investment_full": False,
+ "refresh_trader_with_dice": True,
+ },
+ "sleep_min": "",
+ "sleep_max": "",
+ "expiring_medicine": True,
+ "weekly_plan": [
+ {"weekday": "周一", "stage": ["PR-A-2"], "medicine": 0},
+ {"weekday": "周二", "stage": ["Annihilation", "PR-B-2"], "medicine": 0},
+ {"weekday": "周三", "stage": ["Annihilation", "PR-D-2"], "medicine": 0},
+ {"weekday": "周四", "stage": ["AP-5"], "medicine": 0},
+ {"weekday": "周五", "stage": ["Annihilation", "1-7"], "medicine": 0},
+ {"weekday": "周六", "stage": ["AP-5"], "medicine": 0},
+ {"weekday": "周日", "stage": ["AP-5"], "medicine": 0},
+ ],
+ "eat_stone": False,
+}
+recruit_config = {
+ "recruit_enable": True,
+ "permit_target": 30,
+ "recruit_robot": False,
+ "recruitment_time": None,
+ "recruit_execution_gap": 2,
+ "recruit_auto_5": True,
+ "recruit_auto_only5": False,
+ "recruit_email_enable": True,
+ "last_execution": None,
+}
+# 模拟器相关设置
+simulator = {
+ "name": "MuMu12",
+ # 多开编号,在模拟器助手最左侧的数字
+ "index": 0,
+ # 用于执行模拟器命令
+ "simulator_folder": "D:\\Program Files\\Netease\\MuMuPlayer-12.0\\shell\\",
+ "wait_time": 25,
+}
+
+# --->>下面是配置信息
+
+# Free (宿舍填充)干员安排黑名单 用英文逗号分开
+free_blacklist = "艾丽妮,但书,龙舌兰"
+
+# 干员宿舍回复阈值
+resting_threshold = 0.5
+
+# 跑单如果all in 贸易站则 不需要修改设置
+# 如果需要无人机加速其他房间则可以修改成房间名字如 'room_1_1'
+drone_room = "room_1_2"
+
+# 设置成0,则不启动葛朗台跑单 设置成数字,单位为秒推荐10s
+run_order_buffer_time = 10
+# 无人机执行间隔时间 (小时)
+drone_execution_gap = 4
+
+# 无人机阈值
+drone_count_limit = 105
+# 跑单延时 单位(分钟)
+run_order_delay = 3.5
+
+reload_room = []
+
+# 基地数据json文件保存名
+state_file_name = "state.json"
+
+# 邮件时差调整
+timezone_offset = 0
+
+# 重要!!! 请将排班表的文本文件复制并且替换成plan 参数
+plan = {}
+plan1 = {}
+plan_config = PlanConfig(
+ plan["conf"]["rest_in_full"],
+ plan["conf"]["exhaust_require"],
+ plan["conf"]["resting_priority"],
+ ling_xi=plan["conf"]["ling_xi"],
+ workaholic=plan["conf"]["workaholic"],
+ max_resting_count=plan["conf"]["max_resting_count"],
+ free_blacklist=free_blacklist,
+ resting_threshold=resting_threshold,
+ run_order_buffer_time=run_order_buffer_time,
+ refresh_trading_config=plan["conf"]["refresh_trading"],
+)
+for room, obj in plan[plan["default"]].items():
+ plan1[room] = [
+ Room(op["agent"], op["group"], op["replacement"]) for op in obj["plans"]
+ ]
+# 默认任务
+plan["default_plan"] = Plan(plan1, plan_config)
+# 备用自定义任务
+plan["backup_plans"] = plan["backup_plans"]
+
+logger.debug(plan)
+
+
+def debuglog():
+ """
+ 在屏幕上输出调试信息,方便调试和报错
+ """
+ logger.handlers[0].setLevel("DEBUG")
+
+
+def savelog():
+ """
+ 指定日志和截屏的保存位置,方便调试和报错
+ 调试信息和截图默认保存在代码所在的目录下
+ """
+ config.LOGFILE_PATH = "./log1"
+ config.SCREENSHOT_PATH = "./screenshot1"
+ config.SCREENSHOT_MAXNUM = 200
+ config.ADB_DEVICE = maa_config["maa_adb"]
+ config.ADB_CONNECT = maa_config["maa_adb"]
+ config.MAX_RETRYTIME = 3
+ config.PASSWORD = "你的密码"
+ config.APPNAME = "com.hypergryph.arknights" # 官服
+ config.TAP_TO_LAUNCH["enable"] = False
+ config.TAP_TO_LAUNCH["x"], config.TAP_TO_LAUNCH["y"] = 0, 0
+ # com.hypergryph.arknights.bilibili # Bilibili 服
+ config.ADB_BINARY = ["F:\\MAA-v4.20.0-win-x64\\adb\\platform-tools\\adb.exe"]
+ config.log_queue = None
+ config.stop_mower = None
+ config.get_scene = {"concurrent": False, "max_workers": 99}
+ config.conf = {"simulator": simulator}
+ config.droidcast = {"enable": False}
+ init_fhlr()
+
+
+def initialize(tasks, scheduler=None):
+ if scheduler is not None:
+ scheduler.handle_error(True)
+ return scheduler
+
+ if scheduler is None:
+ base_scheduler = BaseSchedulerSolver()
+ base_scheduler.package_name = config.APPNAME
+ base_scheduler.operators = {}
+ base_scheduler.global_plan = plan
+ base_scheduler.current_base = {}
+ base_scheduler.resting = []
+ base_scheduler.tasks = tasks
+ base_scheduler.last_room = ""
+ base_scheduler.MAA = None
+ base_scheduler.send_message_config = send_message_config
+ base_scheduler.ADB_CONNECT = config.ADB_CONNECT[0]
+ # 以下赋值需要本地修改源码 base_schedule
+ base_scheduler.maa_config = maa_config
+ base_scheduler.recruit_config = recruit_config
+ base_scheduler.error = False
+ base_scheduler.drone_count_limit = drone_count_limit # 无人机高于于该值时才使用
+ base_scheduler.drone_room = drone_room
+ base_scheduler.drone_execution_gap = drone_execution_gap
+ base_scheduler.run_order_delay = run_order_delay # 跑单提前10分钟运行
+ base_scheduler.reload_room = reload_room
+ return base_scheduler
+
+
+def save_state():
+ with open(state_file_name, "w") as f:
+ if base_scheduler is not None and base_scheduler.op_data is not None:
+ json.dump(vars(base_scheduler.op_data), f, default=str)
+
+
+def load_state(base_scheduler):
+ try:
+ if not os.path.exists(state_file_name):
+ return
+ with open(state_file_name, "r") as f:
+ state = json.load(f)
+ operators = {k: eval(v) for k, v in state["operators"].items()}
+ for k, v in operators.items():
+ if not v.time_stamp == "None":
+ v.time_stamp = datetime.strptime(v.time_stamp, "%Y-%m-%d %H:%M:%S.%f")
+ else:
+ v.time_stamp = None
+ for k, v in operators.items():
+ if k in base_scheduler.op_data.operators:
+ # 只复制心情数据
+ base_scheduler.op_data.operators[k].mood = v.mood
+ base_scheduler.op_data.operators[k].time_stamp = v.time_stamp
+ base_scheduler.op_data.operators[k].depletion_rate = v.depletion_rate
+ base_scheduler.op_data.operators[k].current_room = v.current_room
+ base_scheduler.op_data.operators[k].current_index = v.current_index
+ # 复制缓存的宿舍数据
+ dorms = [eval(v) for v in state["dorm"]]
+ for v in dorms:
+ if not v.time == "None":
+ v.time = datetime.strptime(v.time, "%Y-%m-%d %H:%M:%S.%f")
+ else:
+ v.time = None
+ base_scheduler.op_data.dorm = dorms
+ # 复制缓存的派对数据
+ if state["party_time"] is not None:
+ base_scheduler.party_time = datetime.strptime(
+ state["party_time"], "%Y-%m-%d %H:%M:%S.%f"
+ )
+ except Exception as ex:
+ logger.error(ex)
+
+
+def simulate():
+ """
+ 具体调用方法可见各个函数的参数说明
+ """
+ global ope_list, base_scheduler
+ # 第一次执行任务
+ taskstr = "SchedulerTask(time='2023-09-12 21:35:43.278494',task_plan={},task_type=TaskTypes.MAA_MALL,meta_data='')"
+ tasks = [eval(t) for t in taskstr.split("||")]
+ for t in tasks:
+ t.time = datetime.strptime(t.time, "%Y-%m-%d %H:%M:%S.%f")
+ reconnect_max_tries = 10
+ reconnect_tries = 0
+ success = False
+ while not success:
+ try:
+ base_scheduler = initialize(tasks)
+ success = True
+ except Exception as E:
+ reconnect_tries += 1
+ logger.exception(E)
+ if reconnect_tries < 3:
+ restart_simulator(simulator)
+ continue
+ else:
+ raise E
+ if base_scheduler.recog.h != 1080 or base_scheduler.recog.w != 1920:
+ logger.error("模拟器分辨率不为1920x1080")
+ return
+ validation_msg = base_scheduler.initialize_operators()
+ if validation_msg is not None:
+ logger.error(validation_msg)
+ return
+ base_scheduler.op_data.first_init = False
+ if len(base_scheduler.op_data.backup_plans) > 0:
+ conditions = base_scheduler.op_data.generate_conditions(
+ len(base_scheduler.op_data.backup_plans)
+ )
+ for con in conditions:
+ validation_msg = base_scheduler.op_data.swap_plan(con, True)
+ if validation_msg is not None:
+ logger.error(f"替换排班验证错误:{validation_msg}, 附表条件为 {con}")
+ return
+ base_scheduler.op_data.swap_plan(
+ [False] * len(base_scheduler.op_data.backup_plans), True
+ )
+ # 验证完排班表以后载入缓存数据加入
+ load_state(base_scheduler)
+ # 根据现有的缓存数据切换至对应附表
+ base_scheduler.backup_plan_solver()
+ while True:
+ try:
+ if len(base_scheduler.tasks) > 0:
+ (base_scheduler.tasks.sort(key=lambda x: x.time, reverse=False))
+ sleep_time = (
+ base_scheduler.tasks[0].time - datetime.now()
+ ).total_seconds()
+ logger.info("||".join([str(t) for t in base_scheduler.tasks]))
+ body = task_template.render(
+ tasks=[obj.format(timezone_offset) for obj in base_scheduler.tasks],
+ base_scheduler=base_scheduler,
+ )
+ base_scheduler.send_message(body, "", "html")
+ # 如果任务间隔时间超过9分钟则启动MAA
+ if sleep_time > 540:
+ if base_scheduler.recruit_config["recruit_enable"] == 1:
+ base_scheduler.recruit_plan_solver()
+ base_scheduler.maa_plan_solver()
+ elif sleep_time > 0:
+ time.sleep(sleep_time)
+ if (
+ len(base_scheduler.tasks) > 0
+ and base_scheduler.tasks[0].type.value.split("_")[0] == "maa"
+ ):
+ base_scheduler.maa_plan_solver(
+ (base_scheduler.tasks[0].type.value.split("_")[1]).split(","),
+ one_time=True,
+ )
+ continue
+ base_scheduler.run()
+ reconnect_tries = 0
+ except (
+ ConnectionError,
+ ConnectionAbortedError,
+ AttributeError,
+ RuntimeError,
+ ):
+ reconnect_tries += 1
+ if reconnect_tries < reconnect_max_tries:
+ logger.warning("连接端口断开....正在重连....")
+ connected = False
+ while not connected:
+ try:
+ base_scheduler = initialize([], base_scheduler)
+ break
+ except Exception as ce:
+ logger.error(ce)
+ restart_simulator(simulator)
+ continue
+ continue
+ continue
+ except RuntimeError:
+ restart_simulator(simulator)
+ except Exception as E:
+ logger.exception(f"程序出错--->{E}")
+
+ # cli.credit() # 信用
+ # ope_lists = cli.ope(eliminate=True, plan=ope_lists) # 行动,返回未完成的作战计划
+ # cli.shop(shop_priority) # 商店
+ # cli.recruit() # 公招
+ # cli.mission() # 任务
+
+
+# debuglog()
+atexit.register(save_state)
+savelog()
+rapidocr.initialize_ocr()
+simulate()
diff --git a/README.md b/README.md
index 86f2b7b2e..aad4555fa 100644
--- a/README.md
+++ b/README.md
@@ -1,312 +1,33 @@
-
-
-![logo](https://github.com/Konano/arknights-mower/raw/main/logo.png)
-
# arknights-mower
-[![GitHub License](https://img.shields.io/github/license/Konano/arknights-mower?style=flat-square)](https://github.com/Konano/arknights-mower/blob/master/LICENSE)
-[![PyPI](https://img.shields.io/pypi/v/arknights-mower?style=flat-square)](https://pypi.org/project/arknights-mower/)
-[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/arknights-mower?style=flat-square)](https://pypi.org/project/arknights-mower/)
-[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/Konano/arknights-mower/Upload%20PyPI?style=flat-square)](https://github.com/Konano/arknights-mower/actions/workflows/python-publish.yml)
-![GitHub last commit (branch)](https://img.shields.io/github/last-commit/Konano/arknights-mower/main?style=flat-square)
-[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/Konano/arknights-mower?style=flat-square)](https://codeclimate.com/github/Konano/arknights-mower)
-
-7*24 小时不间断长草,让你忘掉这个游戏!
-
-
-
-## ⚠ 注意事项
-
-- 本程序不支持国服以外的明日方舟区服,支持官服和 Bilibili 服。
-- 原理上,使用本程序没有任何被判定为作弊并被封号的风险,但是作者不对使用此程序造成的任何损失负责。
-- 开发作者课业繁重,有时不太能及时反馈和修复 Bug,见谅一下。
-
-## 主要功能
-
-- 自动打开《明日方舟》,支持官服和 Bilibili 服
-- 自动登录
- - 账户密码需要手动输入
-- 自动访友收取信用点
-- 自动前往信用商店,领取信用点并按指定优先级购买商品
-- 自动确认任务完成
-- 自动刷体力
- - 默认进行上一次完成的关卡
- - 可设置成优先完成剿灭任务
- - 可自动通过体力药剂和源石回复体力
- - 可限定关卡次数和序列
- - 可指定关卡,包括主线、插曲、别传和资源收集四大区域
-- 自动完成公招
-- 自动收取邮件奖励
-- 自动收取并安放线索
-- 自动消耗无人机加速制造站或贸易站
-- 自动更换基建排班干员(建议搭配配置文件使用, 也可命令行直接输入)
-- 自动使用菲亚梅塔恢复指定房间心情最低干员的心情并重回岗位(工作位置不变以避免重新暖机) [[参考使用场景](https://www.bilibili.com/video/BV1mZ4y1z7wx)]
-- 支持游戏任意分辨率(低于 1080p 的分辨率可能会有一些问题)
-
-## 安装
-
-程序分为 Pypi 版本和 Windows 可执行文件版本两种。
-
-Pypi 版本可通过 pip 直接安装:
-
-```bash
-pip3 install arknights-mower
-```
-
-也可以在 [Releases](https://github.com/Konano/arknights-mower/releases) 下载 Windows 可执行文件版本。
-
-## 运行须知
-
-运行脚本需要安装 ADB 并与安卓模拟器进行连接。
-
-### ADB 配置
-
-ADB 下载地址:
-- Windows: https://dl.google.com/android/repository/platform-tools-latest-windows.zip
-- Mac: https://dl.google.com/android/repository/platform-tools-latest-darwin.zip
-- Linux: https://dl.google.com/android/repository/platform-tools-latest-linux.zip
-
-下载 ADB 后需要将 ADB 所在目录添加到环境变量中。请确认 `adb devices` 中列出了目标模拟器或设备:
-
-```
-$ adb devices
-emulator-5554 device
-```
-
-Windows 下的夜神(Nox)模拟器会自动启动并连接自带的 ADB,如果对配置 ADB 比较苦手的人可以换用夜神(Nox)模拟器。
-
-### 模拟器配置
-
-_TODO:因为各家模拟器的运行情况都不一样,所以有必要梳理一下各家模拟器如何搭配程序使用,此处留一坑待填_
-
-Linux 下可以使用 Anbox 来运行 Android 模拟器,[参见教程](https://www.cnblogs.com/syisyuan/p/12811595.html)。
-
-## 使用教程
-
-第一次运行程序时,会在可执行文件的同目录(Windows 可执行文件版本)或 Home 目录(Linux 下为 `~/`,Windows 下为 `%HOMEPATH%` 或 `C:/Users/你的用户名/`)处生成配置文件 `config.yaml`(Windows 可执行文件版本)或 `.ark_mower.yaml`(Pypi 版本)。
-
-**强烈建议在开始使用程序前仔细阅读配置文件内的注释说明,并根据自身情况修改配置文件中各项的值,否则可能无法正常运行。**
-
-Pypi 版本只支持命令行模式启动,Windows 可执行文件版本支持命令行模式启动和双击文件启动。
-
-直接双击运行 Windows 可执行文件版本将会执行配置文件内所设定的计划任务,自动进行收邮件、收信用点、收基建产物、刷体力等操作。
-
-若想更进一步指定运行的功能,则需要通过命令行模式启动。Windows 可执行文件版本需要通过 cmd 或 powershell 指定命令行运行的参数。
-
-命令行模式下的使用说明如下:
-
-```
-$ arknights-mower
-usage: arknights-mower command [command args] [--config filepath] [--debug]
-commands (prefix abbreviation accepted):
- base [plan] [-c] [-d[F][N]] [-f[F][N]]
- 自动处理基建的信赖/货物/订单/线索/无人机
- plan 自动更换基建排班干员(建议搭配配置文件使用,也可命令行直接输入)
- -c 是否自动收集并使用线索
- -d 是否自动消耗无人机,F 表示第几层(1-3),N 表示从左往右第几个房间(1-3)
- -f 是否自动使用菲亚梅塔恢复指定房间心情最差干员的心情并恢复原位,F、N 含义同上
- credit
- 自动访友获取信用点
- mail
- 自动收取邮件
- mission
- 收集每日任务和每周任务奖励
- shop [items ...]
- 自动前往商店消费信用点
- items 优先考虑的物品,若不指定则使用配置文件中的优先级,默认为从上到下从左到右购买
- recruit [agents ...]
- 自动进行公共招募
- agents 优先考虑的公招干员,若不指定则使用配置文件中的优先级,默认为高稀有度优先
- operation [level] [n] [-r[N]] [-R[N]] [-e|-E]
- 自动进行作战,可指定次数或直到理智不足
- level 指定关卡名称,未指定则默认前往上一次关卡
- n 指定作战次数,未指定则默认作战直到理智不足
- -r 是否自动回复理智,最多回复 N 次,N 未指定则表示不限制回复次数
- -R 是否使用源石回复理智,最多回复 N 次,N 未指定则表示不限制回复次数
- -e 是否优先处理未完成的每周剿灭,优先使用代理卡;-E 表示只使用代理卡而不消耗理智
- operation --plan
- (使用配置文件中的参数以及计划)自动进行作战
- version
- 输出版本信息
- help
- 输出本段消息
- schedule
- 执行配置文件中的计划任务
- --debug
- 启用调试功能,调试信息将会输出到 /root/work/arknights-mower/log 中
- --config filepath
- 指定配置文件,默认使用 /root/work/arknights-mower/config.yaml
-```
-
-命令行模式下的具体使用例子如下:
-
-```
-arknights-mower operation
-# 重复刷上一次关卡,直到理智不足停止
-arknights-mower operation 99
-# 重复刷上一次关卡 99 次
-arknights-mower operation -r5
-# 重复刷上一次关卡,使用理智药自动回复理智,最多消耗 5 瓶(直到理智不足停止)
-arknights-mower operation -r
-# 重复刷上一次关卡,使用理智药自动回复理智,直到理智药用完为止
-arknights-mower operation 1-7 99 -R5
-# 重复刷 1-7 关卡 99 次,使用源石自动回复理智,最多消耗 5 颗
-arknights-mower operation GT-1 99 -r5 -R5
-# 重复刷 GT-1 关卡 99 次,使用理智药以及源石自动回复理智,最多消耗 5 瓶理智药和 5 颗源石
-arknights-mower recruit 因陀罗 火神
-# 公招自动化,优先选择保底星数高的组合,若有多种标签组合保底星数一致则优先选择包含优先级高的干员的组合,公招干员的优先级从高到低分别是因陀罗和火神,默认为高稀有度优先
-arknights-mower shop 招聘许可 赤金 龙门币
-# 在商场使用信用点消费,购买物品的优先级从高到低分别是招聘许可、赤金和龙门币,其余物品不购买
-arknights-mower base -f12 plan_2
-# 自动使用菲亚梅塔恢复B102房间心情最差干员的心情,并保持原位;自动进行配置文件中名为`plan_2`的的基建排班
-arknights-mower base -c -d33
-# 自动收取基建中的信赖/货物/订单;自动放置线索;自动前往 B303 房间(地下 3 层从左往右数第 3 间)使用无人机加速生产或贸易订单;
-arknights-mower base room_1_2 柏喙 巫恋 龙舌兰 contact 絮雨 dormitory_1 杜林 Free Free Free Free
-# 自动更换基建B102房间干员为 柏喙 巫恋 龙舌兰, 更换办公室干员为 絮雨, 更换1号宿舍干员为 杜林和任意4个空闲干员(房间名请参考 base.json)
-```
-
-命令可使用前缀或首字母缩写,如:
-
-```
-arknights-mower h
-# 输出帮助信息
-arknights-mower ope
-# 重复刷上一次关卡,直到理智不足停止
-arknights-mower o 1-7 99 -r5 -R5
-# 重复刷 1-7 关卡 99 次,使用理智药以及源石自动回复理智,最多消耗 5 瓶理智药和 5 颗源石
-```
-
-**请注意:基建自动换班需要搭配配置文件使用。**
-
-## 更多高级功能
-
-如果想定制更加复杂的定时计划等其他高级功能,可根据个人需求修改 [diy.py](https://github.com/Konano/arknights-mower/blob/main/diy.py) 并运行,具体见文件内注释说明。
-
-如果想添加其他的功能,你甚至可以创建一个继承 `BaseSolver` 的自定义类,通过现有接口实现自己的想法。这里展示了一种可能的例子:
-
-```python
-from arknights_mower.strategy import Solver
-
-# 自定义基建排班
-# 这里自定义了一套排班策略,实现的是两班倒,分为四个阶段
-# 阶段 1 和 2 为第一班,阶段 3 和 4 为第二班
-# 第一班的干员在阶段 3 和 4 分两批休息,第二班同理
-# 每个阶段耗时 6 小时
-
-# 若菲亚梅塔出现在列表中,则会优先选择菲亚梅塔,以防止其与意料之外的干员交换心情
-
-plan = {
- # 阶段 1
- 'plan_1': {
- # 控制中枢
- 'central': ['夕', '令', '凯尔希', '阿米娅', '玛恩纳'],
- # 办公室
- 'contact': ['艾雅法拉'],
- # 宿舍
- 'dormitory_1': ['杜林', '闪灵', '安比尔', '空弦', '缠丸'],
- 'dormitory_2': ['推进之王', '琴柳', '赫默', '杰西卡', '调香师'],
- 'dormitory_3': ['夜莺', '波登可', '夜刀', '古米', '空爆'],
- 'dormitory_4': ['空', 'Lancet-2', '香草', '史都华德', '刻俄柏'],
- # 会客室
- 'meeting': ['陈', '红'],
- # 制造站 + 贸易站 + 发电站
- 'room_1_1': ['德克萨斯', '能天使', '拉普兰德'],
- 'room_1_2': ['断罪者', '食铁兽', '槐琥'],
- 'room_1_3': ['阿消'],
- 'room_2_1': ['巫恋', '柏喙', '慕斯'],
- 'room_2_2': ['红豆', '霜叶', '白雪'],
- 'room_2_3': ['雷蛇'],
- 'room_3_1': ['Castle-3', '梅尔', '白面鸮'],
- 'room_3_2': ['格雷伊'],
- 'room_3_3': ['砾', '夜烟', '斑点']
- },
- # 阶段 2
- 'plan_2': {
- # 注释掉了部分和阶段 1 一样排班计划的房间,加快排班速度
- # 'contact': ['艾雅法拉'],
- 'dormitory_1': ['杜林', '闪灵', '芬', '稀音', '克洛丝'],
- 'dormitory_2': ['推进之王', '琴柳', '清流', '森蚺', '温蒂'],
- 'dormitory_3': ['夜莺', '波登可', '伊芙利特', '深靛', '炎熔'],
- 'dormitory_4': ['空', 'Lancet-2', '远山', '星极', '普罗旺斯'],
- # 'meeting': ['陈', '红'],
- # 'room_1_1': ['德克萨斯', '能天使', '拉普兰德'],
- # 'room_1_2': ['断罪者', '食铁兽', '槐琥'],
- # 'room_1_3': ['阿消'],
- # 'room_2_1': ['巫恋', '柏喙', '慕斯'],
- # 'room_2_2': ['红豆', '霜叶', '白雪'],
- # 'room_2_3': ['雷蛇'],
- # 'room_3_1': ['Castle-3', '梅尔', '白面鸮'],
- # 'room_3_2': ['格雷伊'],
- # 'room_3_3': ['砾', '夜烟', '斑点']
- },
- 'plan_3': {
- 'contact': ['普罗旺斯'],
- 'dormitory_1': ['杜林', '闪灵', '格雷伊', '雷蛇', '阿消'],
- 'dormitory_2': ['推进之王', '琴柳', '德克萨斯', '能天使', '拉普兰德'],
- 'dormitory_3': ['夜莺', '波登可', '巫恋', '柏喙', '慕斯'],
- 'dormitory_4': ['空', 'Lancet-2', '艾雅法拉', '陈', '红'],
- 'meeting': ['远山', '星极'],
- 'room_1_1': ['安比尔', '空弦', '缠丸'],
- 'room_1_2': ['赫默', '杰西卡', '调香师'],
- 'room_1_3': ['伊芙利特'],
- 'room_2_1': ['夜刀', '古米', '空爆'],
- 'room_2_2': ['香草', '史都华德', '刻俄柏'],
- 'room_2_3': ['深靛'],
- 'room_3_1': ['芬', '稀音', '克洛丝'],
- 'room_3_2': ['炎熔'],
- 'room_3_3': ['清流', '森蚺', '温蒂']
- },
- 'plan_4': {
- # 'contact': ['普罗旺斯'],
- 'dormitory_1': ['杜林', '闪灵', '断罪者', '食铁兽', '槐琥'],
- 'dormitory_2': ['推进之王', '琴柳', '红豆', '霜叶', '白雪'],
- 'dormitory_3': ['夜莺', '波登可', 'Castle-3', '梅尔', '白面鸮'],
- 'dormitory_4': ['空', 'Lancet-2', '砾', '夜烟', '斑点'],
- # 'meeting': ['远山', '星极'],
- # 'room_1_1': ['安比尔', '空弦', '缠丸'],
- # 'room_1_2': ['赫默', '杰西卡', '调香师'],
- # 'room_1_3': ['伊芙利特'],
- # 'room_2_1': ['夜刀', '古米', '空爆'],
- # 'room_2_2': ['香草', '史都华德', '刻俄柏'],
- # 'room_2_3': ['深靛'],
- # 'room_3_1': ['芬', '稀音', '克洛丝'],
- # 'room_3_2': ['炎熔'],
- # 'room_3_3': ['清流', '森蚺', '温蒂']
- }
-}
-
-Solver().base(arrange=plan)
-```
-
-欢迎大家提交 Pull requests 增加更多的功能!
-
-## 常见问题 Q&A
-
-#### 运行时出现错误:An error occurred when loading the configuration file
-
-配置文件出现格式错误,可以结合 [在线 YAML 校验器](https://www.bejson.com/validators/yaml_editor/) 进行检查。
-
-#### 大量出现「识别出了点小差错」并卡死在特定界面
-
-当前版本在非 1080p(1920x1080)分辨率下,对于部分界面的识别可能会出现错误,将模拟器修改为 1080p 分辨率可以解决大部分问题。如果在分辨率修改后问题仍未解决,可以在 Issue 页面提出。
-
-#### 提示「未检测到相应设备。请运行 `adb devices` 确认列表中列出了目标模拟器或设备。」
+[![GitHub License](https://img.shields.io/github/license/ArkMowers/arknights-mower)](https://github.com/ArkMowers/arknights-mower/blob/master/LICENSE)
+[![GitHub last commit (branch)](https://img.shields.io/github/last-commit/ArkMowers/arknights-mower/dev_shawn)](https://github.com/ArkMowers/arknights-mower/commits/dev_shawn/)
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ArkMowers/arknights-mower/pyinstaller-win-shawn.yml?branch=dev_shawn&)](https://github.com/ArkMowers/arknights-mower/actions/workflows/pyinstaller-win-shawn.yml)
+[![download](https://img.shields.io/website?url=https%3A%2F%2Fmower.zhaozuohong.vip&label=Mower%E4%B8%8B%E8%BD%BD%E7%AB%99)](https://mower.zhaozuohong.vip/)
+[![qq_guild](https://img.shields.io/badge/QQ%E9%A2%91%E9%81%93-2r118jwue4-blue)](https://pd.qq.com/s/5t91c3gx9)
-- 夜神(Nox)模拟器:[解决办法](https://github.com/Konano/arknights-mower/issues/117#issuecomment-1118447644)
+Mower 是为长期运行设计的、开源的明日方舟脚本。
-## 遇到报错?想要更多功能?
+## 功能介绍
-如果你在使用过程中遇到问题,欢迎通过提交 Issue 的方式报错或者提问。报告 Issue 时建议附上调试信息以便定位问题。
+- 基建:跑单、按心情动态换班;
+- 森空岛:签到、仓库读取;
+- 日常:公招、邮件、线索、清理智;
+- 大型任务:生息演算、隐秘战线;
+- 签到:五周年月卡、限定池每日一抽、矿区、孤星领箱子、端午签到……
+- 调用 maa:肉鸽、保全。
-也欢迎加入交流群讨论:
+## 界面截图
-- [Telegram Group](https://t.me/joinchat/eFkqRj1IWm9kYTBl)
-- [QQ Group](https://jq.qq.com/?_wv=1027&k=4gWboTVI): 239200680
+![log](./img/log.png)
+![settings](./img/settings.png)
+![plan-editor](./img/plan-editor.png)
+![riic-report](./img/riic-report.png)
-## Star History
+## 下载与安装
-
+Mower 支持 Windows、macOS 与 Linux 平台。Windows 用户推荐从 [mower 下载站](https://mower.zhaozuohong.vip/)下载运行;macOS 与 Linux 用户请参考《[从源码运行 arknights-mower](https://blog.zhaozuohong.vip/2023/08/02/run-arknights-mower-from-source/)》。
-[![Star History Chart](https://api.star-history.com/svg?repos=Konano/arknights-mower&type=Date)](https://star-history.com/#Konano/arknights-mower&Date)
+初次使用,建议阅读《[Mower 简明教程](https://blog.zhaozuohong.vip/2024/05/10/mower-guide/tutorial/)》。
-
+提出建议、反馈 Bug 或交流基建知识,欢迎加入 QQ 频道 ArkMower(频道号:2r118jwue4)。
diff --git a/arknights_mower/__init__.py b/arknights_mower/__init__.py
index 8b1ea5ff6..3d5fcfee9 100644
--- a/arknights_mower/__init__.py
+++ b/arknights_mower/__init__.py
@@ -2,17 +2,15 @@
import sys
from pathlib import Path
-# Use sys.frozen to check if run through pyinstaller frozen exe, and sys._MEIPASS to get temp path.
-if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
- __pyinstall__ = True
- # Why they create a __init__ folder here...idk.
- __rootdir__ = Path(sys._MEIPASS).joinpath('arknights_mower').joinpath('__init__').resolve()
+__version__ = "2024.08"
+
+if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
+ __rootdir__ = Path(sys._MEIPASS).joinpath("arknights_mower").resolve()
else:
- __pyinstall__ = False
__rootdir__ = Path(__file__).parent.resolve()
-# Command line mode
-__cli__ = not (__pyinstall__ and not sys.argv[1:])
+ from arknights_mower.utils.git_rev import revision_info
+
+ __version__ += "+" + revision_info()[:7]
__system__ = platform.system().lower()
-__version__ = '2.1.8'
diff --git a/arknights_mower/__main__.py b/arknights_mower/__main__.py
index a43835188..8802b77ea 100644
--- a/arknights_mower/__main__.py
+++ b/arknights_mower/__main__.py
@@ -1,72 +1,445 @@
-import sys
-import traceback
-from pathlib import Path
-
-from . import __pyinstall__, __rootdir__
-from .command import *
-from .utils import config
-from .utils.device import Device
-from .utils.log import logger, set_debug_mode
-
-
-def main(module: bool = True) -> None:
- args = sys.argv[1:]
- if not args and __pyinstall__:
- logger.info('参数为空,默认执行 schedule 模式,按下 Ctrl+C 以结束脚本运行')
- args.append('schedule')
- config_path = None
- debug_mode = False
+import os
+from datetime import datetime, timedelta
+
+from arknights_mower.solvers.base_schedule import BaseSchedulerSolver
+from arknights_mower.solvers.reclamation_algorithm import ReclamationAlgorithm
+from arknights_mower.solvers.secret_front import SecretFront
+from arknights_mower.utils import config, path, rapidocr
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.datetime import format_time
+from arknights_mower.utils.depot import 创建csv, 创建json
+from arknights_mower.utils.device.adb_client.session import Session
+from arknights_mower.utils.device.scrcpy import Scrcpy
+from arknights_mower.utils.email import send_message, task_template
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.logic_expression import get_logic_exp
+from arknights_mower.utils.operators import Operator
+from arknights_mower.utils.path import get_path
+from arknights_mower.utils.plan import Plan, PlanConfig, Room
+from arknights_mower.utils.simulator import restart_simulator
+
+base_scheduler = None
+
+
+# 执行自动排班
+def main(saved_state):
+ logger.info("开始运行Mower")
+ rapidocr.initialize_ocr()
+ data = None
+ if saved_state != {}:
+ data = saved_state
+ simulate(data)
+
+
+def initialize(
+ tasks: list, scheduler: BaseSchedulerSolver | None = None
+) -> BaseSchedulerSolver:
+ if scheduler:
+ scheduler.handle_error(True)
+ return scheduler
+
+ base_scheduler = BaseSchedulerSolver()
+ plan1 = {}
+ plan = config.plan.model_dump(exclude_none=True)
+ conf = config.conf
+ plan_config = PlanConfig(
+ rest_in_full=config.plan.conf.rest_in_full,
+ exhaust_require=config.plan.conf.exhaust_require,
+ resting_priority=config.plan.conf.resting_priority,
+ ling_xi=config.plan.conf.ling_xi,
+ workaholic=config.plan.conf.workaholic,
+ max_resting_count=config.plan.conf.max_resting_count,
+ free_blacklist=conf.free_blacklist,
+ resting_threshold=conf.resting_threshold,
+ refresh_trading_config=config.plan.conf.refresh_trading,
+ free_room=conf.free_room,
+ )
+ for room, obj in plan[plan["default"]].items():
+ plan1[room] = [
+ Room(op["agent"], op["group"], op["replacement"]) for op in obj["plans"]
+ ]
+ # 默认任务
+ plan["default_plan"] = Plan(plan1, plan_config)
+ # 备用自定义任务
+ backup_plans: list[Plan] = []
+
+ for i in plan["backup_plans"]:
+ backup_plan: dict[str, Room] = {}
+ for room, obj in i["plan"].items():
+ backup_plan[room] = [
+ Room(op["agent"], op["group"], op["replacement"]) for op in obj["plans"]
+ ]
+ backup_config = PlanConfig(
+ i["conf"]["rest_in_full"],
+ i["conf"]["exhaust_require"],
+ i["conf"]["resting_priority"],
+ ling_xi=i["conf"]["ling_xi"],
+ workaholic=i["conf"]["workaholic"],
+ max_resting_count=i["conf"]["max_resting_count"],
+ free_blacklist=i["conf"]["free_blacklist"],
+ resting_threshold=conf.resting_threshold,
+ refresh_trading_config=i["conf"]["refresh_trading"],
+ free_room=conf.free_room,
+ )
+ backup_trigger = get_logic_exp(i["trigger"]) if "trigger" in i else None
+ backup_task = i.get("task")
+ backup_trigger_timing = i.get("trigger_timing")
+ backup_plans.append(
+ Plan(
+ backup_plan,
+ backup_config,
+ trigger=backup_trigger,
+ task=backup_task,
+ trigger_timing=backup_trigger_timing,
+ )
+ )
+ plan["backup_plans"] = backup_plans
+
+ logger.debug(plan)
+ base_scheduler.global_plan = plan
+ base_scheduler.tasks = tasks
+ base_scheduler.enable_party = conf.enable_party == 1 # 是否使用线索
+ base_scheduler.leifeng_mode = conf.leifeng_mode == 1 # 是否有额外线索就送出
+ # 干员宿舍回复阈值
+ # 高效组心情低于 UpperLimit * 阈值 (向下取整)的时候才会会安排休息
+ base_scheduler.last_room = ""
+ # logger.info("宿舍黑名单:" + str(plan_config.free_blacklist))
+ # 估计没用了
+ base_scheduler.MAA = None
+ base_scheduler.error = False
+ base_scheduler.drone_room = None if conf.drone_room == "" else conf.drone_room
+ base_scheduler.reload_room = list(
+ filter(None, conf.reload_room.replace(",", ",").split(","))
+ )
+
+ # 关闭游戏次数计数器
+ base_scheduler.task_count = 0
+
+ return base_scheduler
+
+
+def simulate(saved):
+ """
+ 具体调用方法可见各个函数的参数说明
+ """
+ logger.info(f"正在使用全局配置空间: {path.global_space}")
+ tasks = saved["tasks"] if saved else []
+ reconnect_max_tries = 10
+ reconnect_tries = 0
+ global base_scheduler
+ success = False
+ while not success:
+ try:
+ base_scheduler = initialize([])
+ success = True
+ except MowerExit:
+ return
+ except Exception as e:
+ logger.exception(e)
+ reconnect_tries += 1
+ if reconnect_tries < 3:
+ restart_simulator()
+ base_scheduler.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ base_scheduler.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ base_scheduler.device.control.scrcpy = Scrcpy(
+ base_scheduler.device.client
+ )
+ continue
+ else:
+ raise e
+ # base_scheduler.仓库扫描() #别删了 方便我找
+ validation_msg = base_scheduler.initialize_operators()
+ if validation_msg is not None:
+ logger.error(validation_msg)
+ return
+ if len(base_scheduler.op_data.backup_plans) > 0:
+ conditions = base_scheduler.op_data.generate_conditions(
+ len(base_scheduler.op_data.backup_plans)
+ )
+ for con in conditions:
+ validation_msg = base_scheduler.op_data.swap_plan(con, True)
+ if validation_msg is not None:
+ logger.error(f"替换排班验证错误:{validation_msg}, 附表条件为 {con}")
+ return
+ base_scheduler.op_data.swap_plan(
+ [False] * len(base_scheduler.op_data.backup_plans), True
+ )
+ timezone_offset = 0
+ if saved:
+ try:
+ for k, v in saved["operators"].items():
+ if k not in base_scheduler.op_data.operators:
+ base_scheduler.op_data.add(Operator(k, ""))
+ # 只复制心情数据
+ base_scheduler.op_data.operators[k].mood = v.mood
+ base_scheduler.op_data.operators[k].time_stamp = v.time_stamp
+ base_scheduler.op_data.operators[k].depletion_rate = v.depletion_rate
+ base_scheduler.op_data.operators[k].current_room = v.current_room
+ base_scheduler.op_data.operators[k].current_index = v.current_index
+ base_scheduler.op_data.dorm = saved["dorm"]
+ base_scheduler.party_time = saved["party_time"]
+ except Exception as ex:
+ logger.error(ex)
+ base_scheduler.tasks = tasks
while True:
- if len(args) > 1 and args[-2] == '--config':
- config_path = Path(args[-1])
- args = args[:-2]
- continue
- if len(args) > 0 and args[-1] == '--debug':
- debug_mode = True
- args = args[:-1]
- continue
- break
-
- if config_path is None:
- if __pyinstall__:
- config_path = Path(sys.executable).parent.joinpath('config.yaml')
- elif module:
- config_path = Path.home().joinpath('.ark_mower.yaml')
- else:
- config_path = __rootdir__.parent.joinpath('config.yaml')
- if not config_path.exists():
- config.build_config(config_path, module)
- else:
- if not config_path.exists():
- logger.error(f'The configuration file does not exist: {config_path}')
+ try:
+ if len(base_scheduler.tasks) > 0:
+ (base_scheduler.tasks.sort(key=lambda x: x.time, reverse=False))
+ logger.info("||".join([str(t) for t in base_scheduler.tasks]))
+ remaining_time = (
+ base_scheduler.tasks[0].time - datetime.now()
+ ).total_seconds()
+
+ if remaining_time > 540:
+ # if config.conf.check_for_updates:
+ # logger.info("检查版本更新")
+ # listing = get_listing()
+ # version = __version__.replace("+", "-")
+ # if not any(i.name.startswith(version) for i in listing):
+ # stable = []
+ # testing = []
+ # for i in listing:
+ # name = i.name
+ # if re.fullmatch(r"[0-9]{4}\.[0-9]{2}\.[0-9]+/", name):
+ # stable.append(name[:-1])
+ # elif re.fullmatch(
+ # r"[0-9]{4}\.[0-9]{2}-[0-9a-z]{7}/", name
+ # ):
+ # testing.append(name[:-1])
+ # title = "Mower版本过旧,请及时更新"
+ # logger.error(title)
+ # body = version_template.render(
+ # stable=stable, testing=testing, current=version
+ # )
+ # send_message(body, title, "WARNING")
+
+ # 刷新时间以鹰历为准
+ # if (
+ # base_scheduler.sign_in
+ # < (datetime.now() - timedelta(hours=4)).date()
+ # ):
+ # if base_scheduler.sign_in_plan_solver():
+ # base_scheduler.sign_in = (
+ # datetime.now() - timedelta(hours=4)
+ # ).date()
+
+ if (
+ base_scheduler.daily_visit_friend
+ < (datetime.now() - timedelta(hours=4)).date()
+ ):
+ if base_scheduler.visit_friend_plan_solver():
+ base_scheduler.daily_visit_friend = (
+ datetime.now() - timedelta(hours=4)
+ ).date()
+
+ if (
+ base_scheduler.daily_report
+ < (datetime.now() - timedelta(hours=4)).date()
+ ):
+ if base_scheduler.report_plan_solver():
+ base_scheduler.daily_report = (
+ datetime.now() - timedelta(hours=4)
+ ).date()
+
+ if (
+ config.conf.skland_enable
+ and base_scheduler.daily_skland
+ < (datetime.now() - timedelta(hours=4)).date()
+ ):
+ if base_scheduler.skland_plan_solover():
+ base_scheduler.daily_skland = (
+ datetime.now() - timedelta(hours=4)
+ ).date()
+
+ if (
+ config.conf.check_mail_enable
+ and base_scheduler.daily_mail
+ < (datetime.now() - timedelta(hours=8)).date()
+ ):
+ if base_scheduler.mail_plan_solver():
+ base_scheduler.daily_mail = (
+ datetime.now() - timedelta(hours=8)
+ ).date()
+
+ if config.conf.recruit_enable:
+ base_scheduler.recruit_plan_solver()
+
+ # 应该在maa任务之后
+ def _is_depotscan():
+ import pandas as pd
+
+ path = get_path("@app/tmp/depotresult.csv")
+ if os.path.exists(path):
+ depotinfo = pd.read_csv(path)
+ 仓库识别时间戳 = depotinfo.iloc[-1, 0]
+ return int(仓库识别时间戳)
+ else:
+ logger.info(f"{path} 不存在,新建一个存储仓库物品的csv")
+ now_time = (
+ int(datetime.now().timestamp())
+ - config.conf.maa_gap * 3600
+ )
+ 创建csv()
+ 创建json()
+ return now_time
+
+ if config.conf.maa_depot_enable:
+ dt = int(datetime.now().timestamp()) - _is_depotscan()
+ if dt >= config.conf.maa_gap * 3600:
+ base_scheduler.仓库扫描()
+ else:
+ logger.info(
+ f"仓库扫描未到时间,将在 {config.conf.maa_gap - dt // 3600}小时之内开始扫描"
+ )
+ if config.conf.maa_enable == 1:
+ subject = f"下次任务在{base_scheduler.tasks[0].time.strftime('%H:%M:%S')}"
+ context = f"下一次任务:{base_scheduler.tasks[0].plan}"
+ logger.info(context)
+ logger.info(subject)
+ body = task_template.render(
+ tasks=[
+ obj.format(timezone_offset)
+ for obj in base_scheduler.tasks
+ ],
+ base_scheduler=base_scheduler,
+ )
+ send_message(body, subject)
+ base_scheduler.maa_plan_solver()
+ else:
+ remaining_time = (
+ base_scheduler.tasks[0].time - datetime.now()
+ ).total_seconds()
+ subject = f"休息 {format_time(remaining_time)},到{base_scheduler.tasks[0].time.strftime('%H:%M:%S')}开始工作"
+ context = f"下一次任务:{base_scheduler.tasks[0].plan if len(base_scheduler.tasks[0].plan) != 0 else '空任务' if base_scheduler.tasks[0].type == '' else base_scheduler.tasks[0].type}"
+ logger.info(context)
+ logger.info(subject)
+ base_scheduler.task_count += 1
+ logger.info(f"第{base_scheduler.task_count}次任务结束")
+ if remaining_time > 0:
+ if remaining_time > 300:
+ if config.conf.close_simulator_when_idle:
+ restart_simulator(start=False)
+ elif config.conf.exit_game_when_idle:
+ base_scheduler.device.exit()
+ body = task_template.render(
+ tasks=[
+ obj.format(timezone_offset)
+ for obj in base_scheduler.tasks
+ ],
+ base_scheduler=base_scheduler,
+ )
+ send_message(body, subject)
+ base_scheduler.sleeping = True
+ base_scheduler.sleep(remaining_time)
+ base_scheduler.sleeping = False
+ base_scheduler.check_current_focus()
+
+ elif remaining_time > 0:
+ now_time = datetime.now().time()
+ try:
+ min_time = datetime.strptime(
+ config.conf.maa_rg_sleep_min, "%H:%M"
+ ).time()
+ max_time = datetime.strptime(
+ config.conf.maa_rg_sleep_max, "%H:%M"
+ ).time()
+ if max_time < min_time:
+ rg_sleep = now_time > min_time or now_time < max_time
+ else:
+ rg_sleep = min_time < now_time < max_time
+ except ValueError:
+ rg_sleep = False
+
+ if not rg_sleep:
+ if config.conf.RA:
+ base_scheduler.recog.update()
+ base_scheduler.back_to_index()
+ ra_solver = ReclamationAlgorithm(
+ base_scheduler.device, base_scheduler.recog
+ )
+ ra_solver.run(base_scheduler.tasks[0].time - datetime.now())
+ remaining_time = (
+ base_scheduler.tasks[0].time - datetime.now()
+ ).total_seconds()
+ elif config.conf.SF:
+ base_scheduler.recog.update()
+ base_scheduler.back_to_index()
+ sf_solver = SecretFront(
+ base_scheduler.device, base_scheduler.recog
+ )
+ sf_solver.run(base_scheduler.tasks[0].time - datetime.now())
+ remaining_time = (
+ base_scheduler.tasks[0].time - datetime.now()
+ ).total_seconds()
+
+ subject = f"休息 {format_time(remaining_time)},到{base_scheduler.tasks[0].time.strftime('%H:%M:%S')}开始工作"
+ context = f"下一次任务:{base_scheduler.tasks[0].plan}"
+ logger.info(context)
+ logger.info(subject)
+ base_scheduler.task_count += 1
+ logger.info(f"第{base_scheduler.task_count}次任务结束")
+ if remaining_time > 300:
+ if config.conf.close_simulator_when_idle:
+ restart_simulator(start=False)
+ elif config.conf.exit_game_when_idle:
+ base_scheduler.device.exit()
+ body = task_template.render(
+ tasks=[
+ obj.format(timezone_offset) for obj in base_scheduler.tasks
+ ],
+ base_scheduler=base_scheduler,
+ )
+ send_message(body, subject)
+ base_scheduler.sleeping = True
+ base_scheduler.sleep(remaining_time)
+ base_scheduler.sleeping = False
+ base_scheduler.check_current_focus()
+
+ base_scheduler.run()
+ reconnect_tries = 0
+ except MowerExit:
return
- try:
- logger.info(f'Loading the configuration file: {config_path}')
- config.load_config(config_path)
- except Exception as e:
- logger.error('An error occurred when loading the configuration file')
- raise e
-
- if debug_mode and not config.DEBUG_MODE:
- config.DEBUG_MODE = True
- set_debug_mode()
-
- device = Device()
-
- logger.debug(args)
- if len(args) == 0:
- help()
- else:
- target_cmd = match_cmd(args[0])
- if target_cmd is not None:
- try:
- target_cmd(args[1:], device)
- except ParamError:
- logger.debug(traceback.format_exc())
- help()
- else:
- help()
-
-
-if __name__ == '__main__':
- main(module=True)
+ except (ConnectionError, ConnectionAbortedError, AttributeError) as e:
+ logger.exception(e)
+ reconnect_tries += 1
+ if reconnect_tries < reconnect_max_tries:
+ logger.warning("出现错误.尝试重启Mower")
+ connected = False
+ while not connected:
+ try:
+ base_scheduler = initialize([], base_scheduler)
+ break
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+ base_scheduler.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ base_scheduler.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ base_scheduler.device.control.scrcpy = Scrcpy(
+ base_scheduler.device.client
+ )
+ continue
+ continue
+ else:
+ raise e
+ except RuntimeError as e:
+ logger.exception(f"程序出错-尝试重启模拟器->{e}")
+ restart_simulator()
+ base_scheduler.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ base_scheduler.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ base_scheduler.device.control.scrcpy = Scrcpy(
+ base_scheduler.device.client
+ )
+ except Exception as e:
+ logger.exception(f"程序出错--->{e}")
+ base_scheduler.recog.update()
diff --git a/arknights_mower/command.py b/arknights_mower/command.py
deleted file mode 100644
index 6c6cf814f..000000000
--- a/arknights_mower/command.py
+++ /dev/null
@@ -1,228 +0,0 @@
-from __future__ import annotations
-
-from . import __version__
-from .solvers import *
-from .utils import config
-from .utils.device import Device
-from .utils.log import logger
-from .utils.param import ParamError, parse_operation_params
-
-
-def mail(args: list[str] = [], device: Device = None):
- """
- mail
- 自动收取邮件
- """
- MailSolver(device).run()
-
-
-def base(args: list[str] = [], device: Device = None):
- """
- base [plan] [-c] [-d[F][N]] [-f[F][N]]
- 自动处理基建的信赖/货物/订单/线索/无人机
- plan 表示选择的基建干员排班计划(建议搭配配置文件使用, 也可命令行直接输入)
- -c 是否自动收集并使用线索
- -d 是否自动消耗无人机,F 表示第几层(1-3),N 表示从左往右第几个房间(1-3)
- -f 是否使用菲亚梅塔恢复特定房间干员心情,恢复后恢复原位且工作位置不变,F、N 含义同上
- """
- from .data import base_room_list, agent_list
-
- arrange = None
- clue_collect = False
- drone_room = None
- fia_room = None
- any_room = []
- agents = []
-
- try:
- for p in args:
- if p[0] == '-':
- if p[1] == 'c':
- clue_collect = True
- elif p[1] == 'd':
- assert '1' <= p[2] <= '3'
- assert '1' <= p[3] <= '3'
- drone_room = f'room_{p[2]}_{p[3]}'
- elif p[1] == 'f':
- assert '1' <= p[2] <= '3'
- assert '1' <= p[3] <= '3'
- fia_room = f'room_{p[2]}_{p[3]}'
- elif arrange is None:
- arrange = config.BASE_CONSTRUCT_PLAN.get(p)
- if arrange is None:
- if p in base_room_list:
- any_room.append(p)
- agents.append([])
- elif p in agent_list or 'free' == p.lower():
- agents[-1].append(p)
- except Exception:
- raise ParamError
-
- if arrange is None and any_room is not None and len(agents) > 0:
- arrange = dict(zip(any_room, agents))
-
- BaseConstructSolver(device).run(arrange, clue_collect, drone_room, fia_room)
-
-
-def credit(args: list[str] = [], device: Device = None):
- """
- credit
- 自动访友获取信用点
- """
- CreditSolver(device).run()
-
-
-def shop(args: list[str] = [], device: Device = None):
- """
- shop [items ...]
- 自动前往商店消费信用点
- items 优先考虑的物品,若不指定则使用配置文件中的优先级,默认为从上到下从左到右购买
- """
- if len(args) == 0:
- ShopSolver(device).run(config.SHOP_PRIORITY)
- else:
- ShopSolver(device).run(args)
-
-
-def recruit(args: list[str] = [], device: Device = None):
- """
- recruit [agents ...]
- 自动进行公共招募
- agents 优先考虑的公招干员,若不指定则使用配置文件中的优先级,默认为高稀有度优先
- """
- if len(args) == 0:
- RecruitSolver(device).run(config.RECRUIT_PRIORITY)
- else:
- RecruitSolver(device).run(args)
-
-
-def mission(args: list[str] = [], device: Device = None):
- """
- mission
- 收集每日任务和每周任务奖励
- """
- MissionSolver(device).run()
-
-
-def operation(args: list[str] = [], device: Device = None):
- """
- operation [level] [n] [-r[N]] [-R[N]] [-e|-E]
- 自动进行作战,可指定次数或直到理智不足
- level 指定关卡名称,未指定则默认前往上一次关卡
- n 指定作战次数,未指定则默认作战直到理智不足
- -r 是否自动回复理智,最多回复 N 次,N 未指定则表示不限制回复次数
- -R 是否使用源石回复理智,最多回复 N 次,N 未指定则表示不限制回复次数
- -e 是否优先处理未完成的每周剿灭,优先使用代理卡;-E 表示只使用代理卡而不消耗理智
- operation --plan
- (使用配置文件中的参数以及计划)自动进行作战
- """
-
- if len(args) == 1 and args[0] == "--plan":
- remain_plan = OpeSolver(device).run(None, config.OPE_TIMES, config.OPE_POTION,
- config.OPE_ORIGINITE, config.OPE_ELIMINATE, config.OPE_PLAN)
- config.update_ope_plan(remain_plan)
- return
-
- level, times, potion, originite, eliminate = parse_operation_params(args)
-
- OpeSolver(device).run(level, times, potion, originite, eliminate)
-
-
-def version(args: list[str] = [], device: Device = None):
- """
- version
- 输出版本信息
- """
- print(f'arknights-mower: version: {__version__}')
-
-
-def help(args: list[str] = [], device: Device = None):
- """
- help
- 输出本段消息
- """
- print(
- 'usage: arknights-mower command [command args] [--config filepath] [--debug]')
- print(
- 'commands (prefix abbreviation accepted):')
- for cmd in global_cmds:
- if cmd.__doc__:
- print(' ' + str(cmd.__doc__.strip()))
- else:
- print(' ' + cmd.__name__)
- print(f' --debug\n 启用调试功能,调试信息将会输出到 {config.LOGFILE_PATH} 中')
- print(f' --config filepath\n 指定配置文件,默认使用 {config.PATH}')
-
-
-"""
-commands for schedule
-operation will be replaced by operation_one in ScheduleSolver
-"""
-schedule_cmds = [base, credit, mail, mission, shop, recruit, operation]
-
-
-def add_tasks(solver: ScheduleSolver = None, tag: str = ''):
- """
- 为 schedule 模块添加任务
- """
- plan = config.SCHEDULE_PLAN.get(tag)
- if plan is not None:
- for args in plan:
- args = args.split()
- if 'schedule' in args:
- logger.error(
- 'Found `schedule` in `schedule`. Are you kidding me?')
- raise NotImplementedError
- try:
- target_cmd = match_cmd(args[0], schedule_cmds)
- if target_cmd is not None:
- solver.add_task(tag, target_cmd, args[1:])
- except Exception as e:
- logger.error(e)
-
-
-def schedule(args: list[str] = [], device: Device = None):
- """
- schedule
- 执行配置文件中的计划任务
- 计划执行时会自动存档至本地磁盘,启动时若检测到有存档,则会使用存档内容继续完成计划
- -n 忽略之前中断的计划任务,按照配置文件重新开始新的计划
- """
- new_schedule = False
-
- try:
- for p in args:
- if p[0] == '-':
- if p[1] == 'n':
- new_schedule = True
- except Exception:
- raise ParamError
-
- solver = ScheduleSolver(device)
- if new_schedule or solver.load_from_disk(schedule_cmds, match_cmd) is False:
- if config.SCHEDULE_PLAN is not None:
- for tag in config.SCHEDULE_PLAN.keys():
- add_tasks(solver, tag)
- else:
- logger.warning('empty plan')
- solver.per_run()
- solver.run()
-
-
-# all available commands
-global_cmds = [base, credit, mail, mission, shop,
- recruit, operation, version, help, schedule]
-
-
-def match_cmd(prefix: str, avail_cmds: list[str] = global_cmds):
- """ match command """
- target_cmds = [x for x in avail_cmds if x.__name__.startswith(prefix)]
- if len(target_cmds) == 1:
- return target_cmds[0]
- elif len(target_cmds) == 0:
- print('unrecognized command: ' + prefix)
- return None
- else:
- print('ambiguous command: ' + prefix)
- print('matched commands: ' + ','.join(x.__name__ for x in target_cmds))
- return None
diff --git a/arknights_mower/data/__init__.py b/arknights_mower/data/__init__.py
index c94c0ae7b..f7f66d376 100644
--- a/arknights_mower/data/__init__.py
+++ b/arknights_mower/data/__init__.py
@@ -4,63 +4,78 @@
from .. import __rootdir__
# agents list in Arknights
-agent_list = json.loads(
- Path(f'{__rootdir__}/data/agent.json').read_text('utf-8'))
+agent_list = json.loads(Path(f"{__rootdir__}/data/agent.json").read_text("utf-8"))
# # agents base skills
# agent_base_config = json.loads(
# Path(f'{__rootdir__}/data/agent-base.json').read_text('utf-8'))
# name of each room in the basement
-base_room_list = json.loads(
- Path(f'{__rootdir__}/data/base.json').read_text('utf-8'))
-
+base_room_list = json.loads(Path(f"{__rootdir__}/data/base.json").read_text("utf-8"))
# the camps to which the clue belongs
-clue_name = json.loads(
- Path(f'{__rootdir__}/data/clue.json').read_text('utf-8'))
-
+clue_name = json.loads(Path(f"{__rootdir__}/data/clue.json").read_text("utf-8"))
# goods sold in shop
-shop_items = json.loads(
- Path(f'{__rootdir__}/data/shop.json').read_text('utf-8'))
-
+shop_items = json.loads(Path(f"{__rootdir__}/data/shop.json").read_text("utf-8"))
# collection of the obtained ocr error
-ocr_error = json.loads(
- Path(f'{__rootdir__}/data/ocr.json').read_text('utf-8'))
+ocr_error = json.loads(Path(f"{__rootdir__}/data/ocr.json").read_text("utf-8"))
+agent_arrange_order = json.loads(
+ Path(f"{__rootdir__}/data/arrange_order.json").read_text("utf-8")
+)
# chapter name in English
-chapter_list = json.loads(
- Path(f'{__rootdir__}/data/chapter.json').read_text('utf-8'))
-
+chapter_list = json.loads(Path(f"{__rootdir__}/data/chapter.json").read_text("utf-8"))
# list of supported levels
-level_list = json.loads(
- Path(f'{__rootdir__}/data/level.json').read_text('utf-8'))
-
+level_list = json.loads(Path(f"{__rootdir__}/data/level.json").read_text("utf-8"))
# open zones
-zone_list = json.loads(
- Path(f'{__rootdir__}/data/zone.json').read_text('utf-8'))
-
+zone_list = json.loads(Path(f"{__rootdir__}/data/zone.json").read_text("utf-8"))
# list of supported weekly levels
-weekly_zones = json.loads(
- Path(f'{__rootdir__}/data/weekly.json').read_text('utf-8'))
-
+weekly_zones = json.loads(Path(f"{__rootdir__}/data/weekly.json").read_text("utf-8"))
# list of scene defined
-scene_list = json.loads(
- Path(f'{__rootdir__}/data/scene.json').read_text('utf-8'))
-
+scene_list = json.loads(Path(f"{__rootdir__}/data/scene.json").read_text("utf-8"))
# recruit database
-recruit_agent = json.loads(
- Path(f'{__rootdir__}/data/recruit.json').read_text('utf-8'))
+recruit_agent = json.loads(Path(f"{__rootdir__}/data/recruit.json").read_text("utf-8"))
+
+recruit_result = json.loads(
+ Path(f"{__rootdir__}/data/recruit_result.json").read_text("utf-8")
+)
-recruit_tag = ['资深干员', '高级资深干员']
+key_mapping = json.loads(
+ Path(f"{__rootdir__}/data/key_mapping.json").read_text("utf-8")
+)
+
+recruit_tag = ["资深干员", "高级资深干员"]
for x in recruit_agent.values():
- recruit_tag += x['tags']
+ recruit_tag += x["tags"]
recruit_tag = list(set(recruit_tag))
+
+"""
+按tag分类组合干员
+"""
+
+agent_with_tags = {}
+for item in recruit_tag:
+ agent_with_tags[item] = []
+ for agent in recruit_agent:
+ if {item} < set(recruit_agent[agent]["tags"]):
+ agent_with_tags[item].append(
+ {
+ "id": agent,
+ "name": recruit_agent[agent]["name"],
+ "star": recruit_agent[agent]["stars"],
+ }
+ )
+
+result_template_list = []
+
+for item in recruit_result:
+ for name in recruit_result[item]:
+ result_template_list.append(name)
diff --git a/arknights_mower/data/agent.json b/arknights_mower/data/agent.json
index 975e53411..2b895e04f 100644
--- a/arknights_mower/data/agent.json
+++ b/arknights_mower/data/agent.json
@@ -1,273 +1 @@
-[
- "Lancet-2",
- "Castle-3",
- "THRM-EX",
- "正义骑士号",
- "夜刀",
- "黑角",
- "巡林者",
- "杜林",
- "12F",
- "芬",
- "香草",
- "翎羽",
- "玫兰莎",
- "泡普卡",
- "卡缇",
- "米格鲁",
- "斑点",
- "克洛丝",
- "安德切尔",
- "炎熔",
- "芙蓉",
- "安赛尔",
- "史都华德",
- "梓兰",
- "夜烟",
- "远山",
- "格雷伊",
- "卡达",
- "深靛",
- "布丁",
- "杰西卡",
- "流星",
- "红云",
- "梅",
- "白雪",
- "松果",
- "安比尔",
- "酸糖",
- "讯使",
- "清道夫",
- "红豆",
- "桃金娘",
- "豆苗",
- "杜宾",
- "缠丸",
- "断罪者",
- "霜叶",
- "艾丝黛尔",
- "慕斯",
- "刻刀",
- "宴",
- "芳汀",
- "罗小黑",
- "砾",
- "孑",
- "暗索",
- "末药",
- "嘉维尔",
- "苏苏洛",
- "调香师",
- "清流",
- "褐果",
- "角峰",
- "蛇屠箱",
- "泡泡",
- "古米",
- "坚雷",
- "深海色",
- "地灵",
- "波登可",
- "罗比菈塔",
- "伊桑",
- "阿消",
- "白面鸮",
- "微风",
- "凛冬",
- "德克萨斯",
- "贾维",
- "苇草",
- "野鬃",
- "极境",
- "夜半",
- "晓歌",
- "诗怀雅",
- "鞭刃",
- "芙兰卡",
- "炎客",
- "因陀罗",
- "石英",
- "燧石",
- "拉普兰德",
- "断崖",
- "柏喙",
- "战车",
- "幽灵鲨",
- "布洛卡",
- "星极",
- "铸铁",
- "赤冬",
- "羽毛笔",
- "龙舌兰",
- "蓝毒",
- "白金",
- "灰喉",
- "四月",
- "寒芒克洛丝",
- "陨星",
- "慑砂",
- "送葬人",
- "奥斯塔",
- "阿米娅",
- "苦艾",
- "特米米",
- "天火",
- "惊蛰",
- "星源",
- "蜜蜡",
- "莱恩哈特",
- "薄绿",
- "爱丽丝",
- "炎狱炎熔",
- "蚀清",
- "耶拉",
- "洛洛",
- "至简",
- "梅尔",
- "稀音",
- "赫默",
- "华法琳",
- "亚叶",
- "锡兰",
- "絮雨",
- "图耶",
- "桑葚",
- "蜜莓",
- "濯尘芙蓉",
- "临光",
- "吽",
- "红",
- "槐琥",
- "卡夫卡",
- "乌有",
- "雷蛇",
- "可颂",
- "拜松",
- "火神",
- "石棉",
- "暮落",
- "车尔尼",
- "闪击",
- "暴雨",
- "灰毫",
- "极光",
- "普罗旺斯",
- "守林人",
- "安哲拉",
- "熔泉",
- "埃拉托",
- "承曦格雷伊",
- "崖心",
- "雪雉",
- "初雪",
- "巫恋",
- "真理",
- "格劳克斯",
- "但书",
- "掠风",
- "空",
- "海蒂",
- "月禾",
- "九色鹿",
- "夏栎",
- "狮蝎",
- "绮良",
- "食铁兽",
- "见行者",
- "罗宾",
- "霜华",
- "贝娜",
- "风丸",
- "能天使",
- "空弦",
- "灰烬",
- "黑",
- "鸿雪",
- "远牙",
- "W",
- "菲亚梅塔",
- "早露",
- "迷迭香",
- "假日威龙陈",
- "推进之王",
- "风笛",
- "嵯峨",
- "琴柳",
- "焰尾",
- "伊芙利特",
- "莫斯提马",
- "艾雅法拉",
- "刻俄柏",
- "夕",
- "异客",
- "卡涅利安",
- "澄闪",
- "黑键",
- "灵知",
- "安洁莉娜",
- "铃兰",
- "麦哲伦",
- "浊心斯卡蒂",
- "令",
- "傀影",
- "老鲤",
- "温蒂",
- "阿",
- "歌蕾蒂娅",
- "水月",
- "归溟幽灵鲨",
- "多萝西",
- "闪灵",
- "夜莺",
- "凯尔希",
- "流明",
- "星熊",
- "塞雷娅",
- "瑕光",
- "年",
- "泥岩",
- "森蚺",
- "号角",
- "山",
- "银灰",
- "棘刺",
- "陈",
- "艾丽妮",
- "煌",
- "百炼嘉维尔",
- "史尔特尔",
- "赫拉格",
- "帕拉斯",
- "耀骑士临光",
- "玛恩纳",
- "暴行",
- "空爆",
- "月见夜",
- "猎蜂",
- "杰克",
- "夜魔",
- "格拉尼",
- "斯卡蒂",
- "罗小黑",
- "海沫",
- "铅踝",
- "达格达",
- "明椒",
- "白铁",
- "雪绒",
- "子月",
- "伺夜",
- "斥罪",
- "缄默德克萨斯",
- "焰影苇草",
- "和弦",
- "谜图",
- "重岳",
- "林",
- "火哨",
- "截云",
- "麒麟X夜刀",
- "火龙S黑角",
- "泰拉大陆调查团"
-]
+["芬", "梅", "宴", "砾", "孑", "吽", "红", "空", "黑", "W", "夕", "林", "令", "阿", "黍", "年", "山", "陈", "锏", "煌", "夜刀", "黑角", "杜林", "香草", "翎羽", "卡缇", "斑点", "炎熔", "芙蓉", "梓兰", "夜烟", "远山", "卡达", "深靛", "布丁", "流星", "红云", "白雪", "松果", "酸糖", "铅踝", "跃跃", "讯使", "红豆", "豆苗", "杜宾", "缠丸", "霜叶", "慕斯", "刻刀", "芳汀", "石英", "暗索", "末药", "清流", "褐果", "角峰", "泡泡", "露托", "古米", "坚雷", "地灵", "伊桑", "阿消", "维荻", "微风", "凛冬", "贾维", "青枳", "红隼", "苇草", "野鬃", "极境", "万顷", "夜半", "渡桥", "晓歌", "谜图", "鞭刃", "苍苔", "医生", "炎客", "摩根", "燧石", "断崖", "烈夏", "铎铃", "柏喙", "战车", "星极", "铸铁", "赤冬", "海沫", "奥达", "蓝毒", "白金", "灰喉", "四月", "隐现", "陨星", "慑砂", "截云", "苦艾", "雪绒", "天火", "惊蛰", "星源", "蜜蜡", "寒檀", "薄绿", "和弦", "蚀清", "耶拉", "洛洛", "至简", "折光", "温米", "梅尔", "稀音", "衡沙", "赫默", "亚叶", "锡兰", "絮雨", "图耶", "桑葚", "蜜莓", "刺玫", "明椒", "莎草", "临光", "深律", "森西", "槐琥", "乌有", "雷蛇", "深巡", "可颂", "拜松", "火神", "石棉", "暮落", "闪击", "暴雨", "灰毫", "火哨", "极光", "洋灰", "玫拉", "子月", "熔泉", "冰酿", "空构", "崖心", "雪雉", "杏仁", "初雪", "巫恋", "海霓", "真理", "但书", "小满", "掠风", "海蒂", "月禾", "夏栎", "凛视", "狮蝎", "绮良", "罗宾", "霜华", "贝娜", "风丸", "双月", "锡人", "空弦", "灰烬", "鸿雪", "远牙", "早露", "提丰", "莱伊", "风笛", "嵯峨", "琴柳", "焰尾", "伺夜", "异客", "澄闪", "黑键", "妮芙", "灵知", "铃兰", "魔王", "白铁", "塑心", "傀影", "老鲤", "温蒂", "水月", "艾拉", "闪灵", "夜莺", "流明", "星熊", "瑕光", "泥岩", "斥罪", "森蚺", "号角", "重岳", "银灰", "棘刺", "仇白", "佩佩", "左乐", "止颂", "暴行", "空爆", "猎蜂", "杰克", "夜魔", "巡林者", "12F", "玫兰莎", "泡普卡", "米格鲁", "克洛丝", "安赛尔", "格雷伊", "杰西卡", "安比尔", "清道夫", "桃金娘", "断罪者", "罗小黑", "休谟斯", "嘉维尔", "苏苏洛", "调香师", "蛇屠箱", "深海色", "波登可", "白面鸮", "诗怀雅", "芙兰卡", "莱欧斯", "因陀罗", "达格达", "幽灵鲨", "布洛卡", "导火索", "羽毛笔", "龙舌兰", "送葬人", "奥斯塔", "阿米娅", "特米米", "爱丽丝", "戴菲恩", "阿罗玛", "华法琳", "哈洛德", "卡夫卡", "车尔尼", "守林人", "安哲拉", "埃拉托", "九色鹿", "食铁兽", "见行者", "能天使", "迷迭香", "伊内丝", "刻俄柏", "逻各斯", "麦哲伦", "多萝西", "凯尔希", "塞雷娅", "赫德雷", "艾丽妮", "赫拉格", "帕拉斯", "玛恩纳", "月见夜", "格拉尼", "斯卡蒂", "安德切尔", "史都华德", "艾丝黛尔", "罗比菈塔", "德克萨斯", "齐尔查克", "拉普兰德", "莱恩哈特", "炎狱炎熔", "濯尘芙蓉", "普罗旺斯", "格劳克斯", "菲亚梅塔", "维什戴尔", "娜仁图亚", "推进之王", "缪尔赛思", "伊芙利特", "莫斯提马", "艾雅法拉", "霍尔海雅", "玛露西尔", "卡涅利安", "安洁莉娜", "淬羽赫默", "歌蕾蒂娅", "阿斯卡纶", "焰影苇草", "乌尔比安", "史尔特尔", "薇薇安娜", "正义骑士号", "历阵锐枪芬", "火龙S黑角", "寒芒克洛丝", "承曦格雷伊", "假日威龙陈", "浊心斯卡蒂", "麒麟R夜刀", "琳琅诗怀雅", "归溟幽灵鲨", "涤火杰西卡", "百炼嘉维尔", "耀骑士临光", "圣约送葬人", "缄默德克萨斯", "纯烬艾雅法拉", "THRM-EX", "泰拉大陆调查团", "Lancet-2", "Castle-3", "PhonoR-0", "Friston-3", "U-Official"]
\ No newline at end of file
diff --git a/arknights_mower/data/arrange_order.json b/arknights_mower/data/arrange_order.json
new file mode 100644
index 000000000..d7825b462
--- /dev/null
+++ b/arknights_mower/data/arrange_order.json
@@ -0,0 +1,170 @@
+{
+ "令": [
+ 2,
+ "true"
+ ],
+ "夕": [
+ 2,
+ "true"
+ ],
+ "稀音": [
+ 2,
+ "true"
+ ],
+ "巫恋": [
+ 2,
+ "true"
+ ],
+ "柏喙": [
+ 2,
+ "true"
+ ],
+ "龙舌兰": [
+ 2,
+ "true"
+ ],
+ "佩佩": [
+ 2,
+ "true"
+ ],
+ "空弦": [
+ 2,
+ "true"
+ ],
+ "伺夜": [
+ 2,
+ "true"
+ ],
+ "绮良": [
+ 2,
+ "true"
+ ],
+ "但书": [
+ 2,
+ "true"
+ ],
+ "泡泡": [
+ 2,
+ "true"
+ ],
+ "火神": [
+ 2,
+ "true"
+ ],
+ "黑键": [
+ 2,
+ "true"
+ ],
+ "波登可": [
+ 2,
+ "false"
+ ],
+ "夜莺": [
+ 2,
+ "false"
+ ],
+ "菲亚梅塔": [
+ 2,
+ "false"
+ ],
+ "流明": [
+ 2,
+ "false"
+ ],
+ "纯烬艾雅法拉":[
+ 2,
+ "false"
+ ],
+ "冰酿":[
+ 2,
+ "false"
+ ],
+ "蜜莓": [
+ 2,
+ "false"
+ ],
+ "闪灵": [
+ 2,
+ "false"
+ ],
+ "杜林": [
+ 2,
+ "false"
+ ],
+ "褐果": [
+ 2,
+ "false"
+ ],
+ "车尔尼": [
+ 2,
+ "false"
+ ],
+ "安比尔": [
+ 2,
+ "false"
+ ],
+ "爱丽丝": [
+ 2,
+ "false"
+ ],
+ "桃金娘": [
+ 2,
+ "false"
+ ],
+ "红云": [
+ 2,
+ "true"
+ ],
+ "承曦格雷伊": [
+ 2,
+ "true"
+ ],
+ "乌有": [
+ 2,
+ "true"
+ ],
+ "图耶": [
+ 2,
+ "true"
+ ],
+ "鸿雪": [
+ 2,
+ "true"
+ ],
+ "孑": [
+ 2,
+ "true"
+ ],
+ "清道夫": [
+ 2,
+ "true"
+ ],
+ "临光": [
+ 2,
+ "true"
+ ],
+ "杜宾": [
+ 2,
+ "true"
+ ],
+ "重岳": [
+ 2,
+ "true"
+ ],
+ "坚雷": [
+ 2,
+ "true"
+ ],
+ "伊内丝": [
+ 2,
+ "true"
+ ],
+ "铅踝": [
+ 2,
+ "true"
+ ],
+ "泰拉大陆调查团": [
+ 2,
+ "true"
+ ]
+}
\ No newline at end of file
diff --git a/arknights_mower/data/key_mapping.json b/arknights_mower/data/key_mapping.json
new file mode 100644
index 000000000..845e17e15
--- /dev/null
+++ b/arknights_mower/data/key_mapping.json
@@ -0,0 +1,10096 @@
+{
+ "4002": [
+ "4002",
+ "DIAMOND",
+ "至纯源石",
+ "NORMAL",
+ 10001
+ ],
+ "至纯源石": [
+ "4002",
+ "DIAMOND",
+ "至纯源石",
+ "NORMAL",
+ 10001
+ ],
+ "4003": [
+ "4003",
+ "DIAMOND_SHD",
+ "合成玉",
+ "NORMAL",
+ 10002
+ ],
+ "合成玉": [
+ "4003",
+ "DIAMOND_SHD",
+ "合成玉",
+ "NORMAL",
+ 10002
+ ],
+ "3141": [
+ "3141",
+ "MTL_DIAMOND_SHD",
+ "源石碎片",
+ "NORMAL",
+ 10003
+ ],
+ "源石碎片": [
+ "3141",
+ "MTL_DIAMOND_SHD",
+ "源石碎片",
+ "NORMAL",
+ 10003
+ ],
+ "4001": [
+ "4001",
+ "GOLD",
+ "龙门币",
+ "NORMAL",
+ 10004
+ ],
+ "龙门币": [
+ "4001",
+ "GOLD",
+ "龙门币",
+ "NORMAL",
+ 10004
+ ],
+ "3003": [
+ "3003",
+ "MTL_GOLD3",
+ "赤金",
+ "NORMAL",
+ 10005
+ ],
+ "赤金": [
+ "3003",
+ "MTL_GOLD3",
+ "赤金",
+ "NORMAL",
+ 10005
+ ],
+ "4004": [
+ "4004",
+ "HGG_SHD",
+ "高级凭证",
+ "NORMAL",
+ 10006
+ ],
+ "高级凭证": [
+ "4004",
+ "HGG_SHD",
+ "高级凭证",
+ "NORMAL",
+ 10006
+ ],
+ "4005": [
+ "4005",
+ "LGG_SHD",
+ "资质凭证",
+ "NORMAL",
+ 10007
+ ],
+ "资质凭证": [
+ "4005",
+ "LGG_SHD",
+ "资质凭证",
+ "NORMAL",
+ 10007
+ ],
+ "4006": [
+ "4006",
+ "EXGG_SHD",
+ "采购凭证",
+ "NORMAL",
+ 10008
+ ],
+ "采购凭证": [
+ "4006",
+ "EXGG_SHD",
+ "采购凭证",
+ "NORMAL",
+ 10008
+ ],
+ "EPGS_COIN": [
+ "EPGS_COIN",
+ "EPGS_COIN",
+ "寻访参数模型",
+ "NORMAL",
+ 10101
+ ],
+ "寻访参数模型": [
+ "EPGS_COIN",
+ "EPGS_COIN",
+ "寻访参数模型",
+ "NORMAL",
+ 10101
+ ],
+ "REP_COIN": [
+ "REP_COIN",
+ "REP_COIN",
+ "情报凭证",
+ "NORMAL",
+ 10102
+ ],
+ "情报凭证": [
+ "REP_COIN",
+ "REP_COIN",
+ "情报凭证",
+ "NORMAL",
+ 10102
+ ],
+ "CRISIS_SHOP_COIN": [
+ "CRISIS_SHOP_COIN",
+ "CRISIS_SHOP_COIN",
+ "合约赏金",
+ "NORMAL",
+ 10201
+ ],
+ "合约赏金": [
+ "CRISIS_SHOP_COIN",
+ "CRISIS_SHOP_COIN",
+ "合约赏金",
+ "NORMAL",
+ 10201
+ ],
+ "CRISIS_SHOP_COIN_V2": [
+ "CRISIS_SHOP_COIN_V2",
+ "CRISIS_SHOP_COIN_V2",
+ "晶体合约赏金",
+ "NORMAL",
+ 10202
+ ],
+ "晶体合约赏金": [
+ "CRISIS_SHOP_COIN_V2",
+ "CRISIS_SHOP_COIN_V2",
+ "晶体合约赏金",
+ "NORMAL",
+ 10202
+ ],
+ "STORY_REVIEW_COIN": [
+ "STORY_REVIEW_COIN",
+ "STORY_REVIEW_COIN",
+ "事相碎片",
+ "NORMAL",
+ 10203
+ ],
+ "事相碎片": [
+ "STORY_REVIEW_COIN",
+ "STORY_REVIEW_COIN",
+ "事相碎片",
+ "NORMAL",
+ 10203
+ ],
+ "renamingCard": [
+ "renamingCard",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20001
+ ],
+ "ID信息更新卡": [
+ "renamingCard_30_050",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20022
+ ],
+ "renamingCard_0_018": [
+ "renamingCard_0_018",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20011
+ ],
+ "renamingCard_30_018": [
+ "renamingCard_30_018",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20012
+ ],
+ "renamingCard_0_023": [
+ "renamingCard_0_023",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20013
+ ],
+ "renamingCard_30_023": [
+ "renamingCard_30_023",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20014
+ ],
+ "renamingCard_0_027": [
+ "renamingCard_0_027",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20015
+ ],
+ "renamingCard_30_027": [
+ "renamingCard_30_027",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20016
+ ],
+ "renamingCard_0_033": [
+ "renamingCard_0_033",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20015
+ ],
+ "renamingCard_30_033": [
+ "renamingCard_30_033",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20016
+ ],
+ "renamingCard_0_038": [
+ "renamingCard_0_038",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20017
+ ],
+ "renamingCard_30_038": [
+ "renamingCard_30_038",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20018
+ ],
+ "renamingCard_0_044": [
+ "renamingCard_0_044",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20019
+ ],
+ "renamingCard_30_044": [
+ "renamingCard_30_044",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20020
+ ],
+ "renamingCard_0_050": [
+ "renamingCard_0_050",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20021
+ ],
+ "renamingCard_30_050": [
+ "renamingCard_30_050",
+ "renamingCard",
+ "ID信息更新卡",
+ "CONSUME",
+ 20022
+ ],
+ "ap_supply_lt_100": [
+ "ap_supply_lt_100",
+ "ap_supply_lt_100",
+ "应急理智顶液",
+ "CONSUME",
+ 30002
+ ],
+ "应急理智顶液": [
+ "ap_supply_lt_100",
+ "ap_supply_lt_100",
+ "应急理智顶液",
+ "CONSUME",
+ 30002
+ ],
+ "ap_supply_lt_010": [
+ "ap_supply_lt_010",
+ "ap_supply_lt_10",
+ "应急理智小样",
+ "CONSUME",
+ 30004
+ ],
+ "应急理智小样": [
+ "ap_supply_lt_010",
+ "ap_supply_lt_10",
+ "应急理智小样",
+ "CONSUME",
+ 30004
+ ],
+ "ap_supply_lt_120": [
+ "ap_supply_lt_120",
+ "ap_supply_lt_120",
+ "应急理智浓缩液",
+ "CONSUME",
+ 30001
+ ],
+ "应急理智浓缩液": [
+ "ap_supply_lt_120",
+ "ap_supply_lt_120",
+ "应急理智浓缩液",
+ "CONSUME",
+ 30001
+ ],
+ "ap_supply_lt_80": [
+ "ap_supply_lt_80",
+ "ap_supply_lt_80",
+ "应急理智加强剂",
+ "CONSUME",
+ 30003
+ ],
+ "应急理智加强剂": [
+ "ap_supply_lt_80",
+ "ap_supply_lt_80",
+ "应急理智加强剂",
+ "CONSUME",
+ 30003
+ ],
+ "EXTERMINATION_AGENT": [
+ "EXTERMINATION_AGENT",
+ "EXTERMINATION_AGENT",
+ "PRTS剿灭代理卡",
+ "NORMAL",
+ 30200
+ ],
+ "PRTS剿灭代理卡": [
+ "EXTERMINATION_AGENT",
+ "EXTERMINATION_AGENT",
+ "PRTS剿灭代理卡",
+ "NORMAL",
+ 30200
+ ],
+ "7004": [
+ "7004",
+ "TKT_GACHA_10",
+ "十连寻访凭证",
+ "NORMAL",
+ 40001
+ ],
+ "十连寻访凭证": [
+ "7004",
+ "TKT_GACHA_10",
+ "十连寻访凭证",
+ "NORMAL",
+ 40001
+ ],
+ "LINKAGE_TKT_GACHA_10_5401": [
+ "LINKAGE_TKT_GACHA_10_5401",
+ "LINKAGE_TKT_GACHA_10_5401",
+ "好好吃饭寻访凭证",
+ "NORMAL",
+ 40003
+ ],
+ "好好吃饭寻访凭证": [
+ "LINKAGE_TKT_GACHA_10_5401",
+ "LINKAGE_TKT_GACHA_10_5401",
+ "好好吃饭寻访凭证",
+ "NORMAL",
+ 40003
+ ],
+ "7003": [
+ "7003",
+ "TKT_GACHA",
+ "寻访凭证",
+ "NORMAL",
+ 40011
+ ],
+ "寻访凭证": [
+ "7003",
+ "TKT_GACHA",
+ "寻访凭证",
+ "NORMAL",
+ 40011
+ ],
+ "7001": [
+ "7001",
+ "TKT_RECRUIT",
+ "招聘许可",
+ "NORMAL",
+ 40012
+ ],
+ "招聘许可": [
+ "7001",
+ "TKT_RECRUIT",
+ "招聘许可",
+ "NORMAL",
+ 40012
+ ],
+ "7002": [
+ "7002",
+ "TKT_INST_FIN",
+ "加急许可",
+ "NORMAL",
+ 40013
+ ],
+ "加急许可": [
+ "7002",
+ "TKT_INST_FIN",
+ "加急许可",
+ "NORMAL",
+ 40013
+ ],
+ "voucher_item_4pick1": [
+ "voucher_item_4pick1",
+ "voucher_item_4pick1",
+ "干员兑换券",
+ "CONSUME",
+ 50001
+ ],
+ "干员兑换券": [
+ "voucher_item_4pick1_1803",
+ "voucher_item_4pick1",
+ "干员兑换券",
+ "CONSUME",
+ 50002
+ ],
+ "voucher_item_4pick1_1803": [
+ "voucher_item_4pick1_1803",
+ "voucher_item_4pick1",
+ "干员兑换券",
+ "CONSUME",
+ 50002
+ ],
+ "voucher_recruitR5_pick2": [
+ "voucher_recruitR5_pick2",
+ "voucher_recruitR5_pick2",
+ "火神因陀罗招募券",
+ "CONSUME",
+ 50003
+ ],
+ "火神因陀罗招募券": [
+ "voucher_recruitR5_pick2",
+ "voucher_recruitR5_pick2",
+ "火神因陀罗招募券",
+ "CONSUME",
+ 50003
+ ],
+ "voucher_recruitR5_pick1803": [
+ "voucher_recruitR5_pick1803",
+ "voucher_recruitR5_g",
+ "资深干员调用凭证",
+ "CONSUME",
+ 50004
+ ],
+ "资深干员调用凭证": [
+ "voucher_recruitR5_pick5001",
+ "voucher_recruitR5_g",
+ "资深干员调用凭证",
+ "CONSUME",
+ 50013
+ ],
+ "voucher_item_pick601": [
+ "voucher_item_pick601",
+ "voucher_item_pick601",
+ "高级干员调用凭证",
+ "CONSUME",
+ 50005
+ ],
+ "高级干员调用凭证": [
+ "voucher_item_pick601",
+ "voucher_item_pick601",
+ "高级干员调用凭证",
+ "CONSUME",
+ 50005
+ ],
+ "voucher_item_pick1401": [
+ "voucher_item_pick1401",
+ "voucher_recruitR6_g",
+ "感谢庆典干员凭证",
+ "CONSUME",
+ 50006
+ ],
+ "感谢庆典干员凭证": [
+ "voucher_item_pick4401",
+ "voucher_recruitR6_g",
+ "感谢庆典干员凭证",
+ "CONSUME",
+ 50014
+ ],
+ "voucher_item_pick1803": [
+ "voucher_item_pick1803",
+ "voucher_recruitR6_g",
+ "周年庆典干员凭证",
+ "CONSUME",
+ 50007
+ ],
+ "周年庆典干员凭证": [
+ "voucher_item_pick5001",
+ "voucher_recruitR6_g",
+ "周年庆典干员凭证",
+ "CONSUME",
+ 50015
+ ],
+ "voucher_item_pick2301": [
+ "voucher_item_pick2301",
+ "voucher_recruitR6_g",
+ "感谢庆典干员凭证",
+ "CONSUME",
+ 50008
+ ],
+ "voucher_item_pick2701": [
+ "voucher_item_pick2701",
+ "voucher_recruitR6_g",
+ "周年庆典干员凭证",
+ "CONSUME",
+ 50009
+ ],
+ "voucher_recruitR5_pick2701": [
+ "voucher_recruitR5_pick2701",
+ "voucher_recruitR5_g",
+ "资深干员调用凭证",
+ "CONSUME",
+ 50010
+ ],
+ "voucher_item_pick3301": [
+ "voucher_item_pick3301",
+ "voucher_recruitR6_g",
+ "感谢庆典干员凭证",
+ "CONSUME",
+ 50011
+ ],
+ "voucher_item_pick3801": [
+ "voucher_item_pick3801",
+ "voucher_recruitR6_g",
+ "周年庆典干员凭证",
+ "CONSUME",
+ 50012
+ ],
+ "voucher_recruitR5_pick3801": [
+ "voucher_recruitR5_pick3801",
+ "voucher_recruitR5_g",
+ "资深干员调用凭证",
+ "CONSUME",
+ 50013
+ ],
+ "voucher_item_pick4401": [
+ "voucher_item_pick4401",
+ "voucher_recruitR6_g",
+ "感谢庆典干员凭证",
+ "CONSUME",
+ 50014
+ ],
+ "voucher_item_pick5001": [
+ "voucher_item_pick5001",
+ "voucher_recruitR6_g",
+ "周年庆典干员凭证",
+ "CONSUME",
+ 50015
+ ],
+ "voucher_recruitR5_pick5001": [
+ "voucher_recruitR5_pick5001",
+ "voucher_recruitR5_g",
+ "资深干员调用凭证",
+ "CONSUME",
+ 50013
+ ],
+ "voucher_recruitR6_pick5301": [
+ "voucher_recruitR6_pick5301",
+ "voucher_recruitR6_pick5301",
+ "高级干员调用凭证-新人",
+ "CONSUME",
+ 50017
+ ],
+ "高级干员调用凭证-新人": [
+ "voucher_recruitR6_pick5301",
+ "voucher_recruitR6_pick5301",
+ "高级干员调用凭证-新人",
+ "CONSUME",
+ 50017
+ ],
+ "voucher_item_4pick1_5301": [
+ "voucher_item_4pick1_5301",
+ "voucher_item_4pick1_5301",
+ "资深干员调用凭证-新人",
+ "CONSUME",
+ 50018
+ ],
+ "资深干员调用凭证-新人": [
+ "voucher_item_4pick1_5301",
+ "voucher_item_4pick1_5301",
+ "资深干员调用凭证-新人",
+ "CONSUME",
+ 50018
+ ],
+ "voucher_levelmax_6": [
+ "voucher_levelmax_6",
+ "voucher_levelmax_6",
+ "高级资深干员特训装置",
+ "CONSUME",
+ 50016
+ ],
+ "高级资深干员特训装置": [
+ "voucher_levelmax_6",
+ "voucher_levelmax_6",
+ "高级资深干员特训装置",
+ "CONSUME",
+ 50016
+ ],
+ "voucher_levelmax_5": [
+ "voucher_levelmax_5",
+ "voucher_levelmax_5",
+ "资深干员特训装置",
+ "CONSUME",
+ 50017
+ ],
+ "资深干员特训装置": [
+ "voucher_levelmax_5",
+ "voucher_levelmax_5",
+ "资深干员特训装置",
+ "CONSUME",
+ 50017
+ ],
+ "voucher_elite_II_6": [
+ "voucher_elite_II_6",
+ "voucher_elite_II_6",
+ "高级资深干员特训邀请函",
+ "CONSUME",
+ 50019
+ ],
+ "高级资深干员特训邀请函": [
+ "voucher_elite_II_6",
+ "voucher_elite_II_6",
+ "高级资深干员特训邀请函",
+ "CONSUME",
+ 50019
+ ],
+ "voucher_elite_II_5": [
+ "voucher_elite_II_5",
+ "voucher_elite_II_5",
+ "资深干员特训邀请函",
+ "CONSUME",
+ 50020
+ ],
+ "资深干员特训邀请函": [
+ "voucher_elite_II_5",
+ "voucher_elite_II_5",
+ "资深干员特训邀请函",
+ "CONSUME",
+ 50020
+ ],
+ "voucher_elite_II_4": [
+ "voucher_elite_II_4",
+ "voucher_elite_II_4",
+ "专业干员特训邀请函",
+ "CONSUME",
+ 50021
+ ],
+ "专业干员特训邀请函": [
+ "voucher_elite_II_4",
+ "voucher_elite_II_4",
+ "专业干员特训邀请函",
+ "CONSUME",
+ 50021
+ ],
+ "voucher_skill_specialLevelMax_6": [
+ "voucher_skill_specialLevelMax_6",
+ "voucher_skill_specialLevelMax_6",
+ "高级资深干员技巧集",
+ "CONSUME",
+ 50022
+ ],
+ "高级资深干员技巧集": [
+ "voucher_skill_specialLevelMax_6",
+ "voucher_skill_specialLevelMax_6",
+ "高级资深干员技巧集",
+ "CONSUME",
+ 50022
+ ],
+ "voucher_skill_specialLevelMax_5": [
+ "voucher_skill_specialLevelMax_5",
+ "voucher_skill_specialLevelMax_5",
+ "资深干员技巧集",
+ "CONSUME",
+ 50023
+ ],
+ "资深干员技巧集": [
+ "voucher_skill_specialLevelMax_5",
+ "voucher_skill_specialLevelMax_5",
+ "资深干员技巧集",
+ "CONSUME",
+ 50023
+ ],
+ "voucher_skill_specialLevelMax_4": [
+ "voucher_skill_specialLevelMax_4",
+ "voucher_skill_specialLevelMax_4",
+ "专业干员技巧集",
+ "CONSUME",
+ 50024
+ ],
+ "专业干员技巧集": [
+ "voucher_skill_specialLevelMax_4",
+ "voucher_skill_specialLevelMax_4",
+ "专业干员技巧集",
+ "CONSUME",
+ 50024
+ ],
+ "voucher_skin": [
+ "voucher_skin",
+ "voucher_skin",
+ "时装自选凭证",
+ "CONSUME",
+ 50051
+ ],
+ "时装自选凭证": [
+ "voucher_skin",
+ "voucher_skin",
+ "时装自选凭证",
+ "CONSUME",
+ 50051
+ ],
+ "voucher_recruitR4_1": [
+ "voucher_recruitR4_1",
+ "voucher_recruitR4_1",
+ "公开招募★4兑换券·I",
+ "CONSUME",
+ 50105
+ ],
+ "公开招募★4兑换券·I": [
+ "voucher_recruitR4_1",
+ "voucher_recruitR4_1",
+ "公开招募★4兑换券·I",
+ "CONSUME",
+ 50105
+ ],
+ "voucher_recruitR3_1": [
+ "voucher_recruitR3_1",
+ "voucher_recruitR3_1",
+ "公开招募★3兑换券·I",
+ "CONSUME",
+ 50106
+ ],
+ "公开招募★3兑换券·I": [
+ "voucher_recruitR3_1",
+ "voucher_recruitR3_1",
+ "公开招募★3兑换券·I",
+ "CONSUME",
+ 50106
+ ],
+ "voucher_chipPack": [
+ "voucher_chipPack",
+ "voucher_chipPack",
+ "芯片组自助打印盒",
+ "CONSUME",
+ 60002
+ ],
+ "芯片组自助打印盒": [
+ "voucher_chipPack",
+ "voucher_chipPack",
+ "芯片组自助打印盒",
+ "CONSUME",
+ 60002
+ ],
+ "voucher_chip": [
+ "voucher_chip",
+ "voucher_chip",
+ "芯片自助打印盒",
+ "CONSUME",
+ 60003
+ ],
+ "芯片自助打印盒": [
+ "voucher_chip",
+ "voucher_chip",
+ "芯片自助打印盒",
+ "CONSUME",
+ 60003
+ ],
+ "voucher_chipPackage": [
+ "voucher_chipPackage",
+ "voucher_chipPackage",
+ "自助芯片组印刻仪",
+ "CONSUME",
+ 60002
+ ],
+ "自助芯片组印刻仪": [
+ "voucher_chipPackage",
+ "voucher_chipPackage",
+ "自助芯片组印刻仪",
+ "CONSUME",
+ 60002
+ ],
+ "voucher_5chipPackage": [
+ "voucher_5chipPackage",
+ "voucher_5chipPackage",
+ "自助芯片印刻仪",
+ "CONSUME",
+ 60002
+ ],
+ "自助芯片印刻仪": [
+ "voucher_5chipPackage",
+ "voucher_5chipPackage",
+ "自助芯片印刻仪",
+ "CONSUME",
+ 60002
+ ],
+ "premium_material_voucher_perm": [
+ "premium_material_voucher_perm",
+ "premium_material_issue_voucher",
+ "特级材料提货券",
+ "CONSUME",
+ 60004
+ ],
+ "特级材料提货券": [
+ "premium_material_issue_voucher",
+ "premium_material_issue_voucher",
+ "特级材料提货券",
+ "CONSUME",
+ 30200
+ ],
+ "advanced_material_voucher_perm": [
+ "advanced_material_voucher_perm",
+ "advanced_material_issue_voucher",
+ "高级材料提货券",
+ "CONSUME",
+ 60005
+ ],
+ "高级材料提货券": [
+ "advanced_material_issue_voucher",
+ "advanced_material_issue_voucher",
+ "高级材料提货券",
+ "CONSUME",
+ 30201
+ ],
+ "randomMaterialRune_0": [
+ "randomMaterialRune_0",
+ "randomMaterialRune_0",
+ "荒芜行动物资补给",
+ "CONSUME",
+ 60011
+ ],
+ "荒芜行动物资补给": [
+ "randomMaterialRune_0",
+ "randomMaterialRune_0",
+ "荒芜行动物资补给",
+ "CONSUME",
+ 60011
+ ],
+ "randomMaterialRune_1": [
+ "randomMaterialRune_1",
+ "randomMaterialRune_1",
+ "黄铁行动物资补给",
+ "CONSUME",
+ 60012
+ ],
+ "黄铁行动物资补给": [
+ "randomMaterialRune_1",
+ "randomMaterialRune_1",
+ "黄铁行动物资补给",
+ "CONSUME",
+ 60012
+ ],
+ "randomMaterialRune_2": [
+ "randomMaterialRune_2",
+ "randomMaterialRune_2",
+ "利刃行动物资补给",
+ "CONSUME",
+ 60013
+ ],
+ "利刃行动物资补给": [
+ "randomMaterialRune_2",
+ "randomMaterialRune_2",
+ "利刃行动物资补给",
+ "CONSUME",
+ 60013
+ ],
+ "randomMaterialRune_3": [
+ "randomMaterialRune_3",
+ "randomMaterialRune_3",
+ "燃灰行动物资补给",
+ "CONSUME",
+ 60014
+ ],
+ "燃灰行动物资补给": [
+ "randomMaterialRune_3",
+ "randomMaterialRune_3",
+ "燃灰行动物资补给",
+ "CONSUME",
+ 60014
+ ],
+ "randomMaterialRune_4": [
+ "randomMaterialRune_4",
+ "randomMaterialRune_4",
+ "铅封行动物资补给",
+ "CONSUME",
+ 60015
+ ],
+ "铅封行动物资补给": [
+ "randomMaterialRune_4",
+ "randomMaterialRune_4",
+ "铅封行动物资补给",
+ "CONSUME",
+ 60015
+ ],
+ "randomMaterialRune_5": [
+ "randomMaterialRune_5",
+ "randomMaterialRune_5",
+ "光谱行动物资补给",
+ "CONSUME",
+ 60016
+ ],
+ "光谱行动物资补给": [
+ "randomMaterialRune_5",
+ "randomMaterialRune_5",
+ "光谱行动物资补给",
+ "CONSUME",
+ 60016
+ ],
+ "randomMaterialRune_6": [
+ "randomMaterialRune_6",
+ "randomMaterialRune_6",
+ "蛮鳞行动物资补给",
+ "CONSUME",
+ 60017
+ ],
+ "蛮鳞行动物资补给": [
+ "randomMaterialRune_6",
+ "randomMaterialRune_6",
+ "蛮鳞行动物资补给",
+ "CONSUME",
+ 60017
+ ],
+ "randomMaterialRune_7": [
+ "randomMaterialRune_7",
+ "randomMaterialRune_7",
+ "松烟行动物资补给",
+ "CONSUME",
+ 60018
+ ],
+ "松烟行动物资补给": [
+ "randomMaterialRune_7",
+ "randomMaterialRune_7",
+ "松烟行动物资补给",
+ "CONSUME",
+ 60018
+ ],
+ "randomMaterialRune_8": [
+ "randomMaterialRune_8",
+ "randomMaterialRune_8",
+ "寻昼行动物资补给",
+ "CONSUME",
+ 60019
+ ],
+ "寻昼行动物资补给": [
+ "randomMaterialRune_8",
+ "randomMaterialRune_8",
+ "寻昼行动物资补给",
+ "CONSUME",
+ 60019
+ ],
+ "randomMaterialRune_9": [
+ "randomMaterialRune_9",
+ "randomMaterialRune_9",
+ "渊默行动物资补给",
+ "CONSUME",
+ 60020
+ ],
+ "渊默行动物资补给": [
+ "randomMaterialRune_9",
+ "randomMaterialRune_9",
+ "渊默行动物资补给",
+ "CONSUME",
+ 60020
+ ],
+ "randomMaterialRune_10": [
+ "randomMaterialRune_10",
+ "randomMaterialRune_10",
+ "尘环行动物资补给",
+ "CONSUME",
+ 60021
+ ],
+ "尘环行动物资补给": [
+ "randomMaterialRune_10",
+ "randomMaterialRune_10",
+ "尘环行动物资补给",
+ "CONSUME",
+ 60021
+ ],
+ "randomMaterialRune_11": [
+ "randomMaterialRune_11",
+ "randomMaterialRune_11",
+ "赝波行动物资补给",
+ "CONSUME",
+ 60022
+ ],
+ "赝波行动物资补给": [
+ "randomMaterialRune_11",
+ "randomMaterialRune_11",
+ "赝波行动物资补给",
+ "CONSUME",
+ 60022
+ ],
+ "randomMaterialRune_12": [
+ "randomMaterialRune_12",
+ "randomMaterialRune_12",
+ "起源行动物资补给",
+ "CONSUME",
+ 60023
+ ],
+ "起源行动物资补给": [
+ "randomMaterialRune_12",
+ "randomMaterialRune_12",
+ "起源行动物资补给",
+ "CONSUME",
+ 60023
+ ],
+ "randomMaterial_1": [
+ "randomMaterial_1",
+ "randomMaterial_1",
+ "罗德岛物资补给",
+ "CONSUME",
+ 60101
+ ],
+ "罗德岛物资补给": [
+ "randomMaterial_1",
+ "randomMaterial_1",
+ "罗德岛物资补给",
+ "CONSUME",
+ 60101
+ ],
+ "randomMaterial_2": [
+ "randomMaterial_2",
+ "randomMaterial_2",
+ "岁过华灯",
+ "CONSUME",
+ 60102
+ ],
+ "岁过华灯": [
+ "randomMaterial_2",
+ "randomMaterial_2",
+ "岁过华灯",
+ "CONSUME",
+ 60102
+ ],
+ "randomMaterial_3": [
+ "randomMaterial_3",
+ "randomMaterial_3",
+ "32h战略配给",
+ "CONSUME",
+ 60103
+ ],
+ "32h战略配给": [
+ "randomMaterial_3",
+ "randomMaterial_3",
+ "32h战略配给",
+ "CONSUME",
+ 60103
+ ],
+ "randomMaterial_4": [
+ "randomMaterial_4",
+ "randomMaterial_4",
+ "感谢庆典物资补给",
+ "CONSUME",
+ 60104
+ ],
+ "感谢庆典物资补给": [
+ "randomMaterial_4",
+ "randomMaterial_4",
+ "感谢庆典物资补给",
+ "CONSUME",
+ 60104
+ ],
+ "randomMaterial_5": [
+ "randomMaterial_5",
+ "randomMaterial_1",
+ "罗德岛物资补给II",
+ "CONSUME",
+ 60105
+ ],
+ "罗德岛物资补给II": [
+ "randomMaterial_5",
+ "randomMaterial_1",
+ "罗德岛物资补给II",
+ "CONSUME",
+ 60105
+ ],
+ "randomMaterial_6": [
+ "randomMaterial_6",
+ "randomMaterial_1",
+ "罗德岛物资补给III",
+ "CONSUME",
+ 60106
+ ],
+ "罗德岛物资补给III": [
+ "randomMaterial_6",
+ "randomMaterial_1",
+ "罗德岛物资补给III",
+ "CONSUME",
+ 60106
+ ],
+ "randomMaterial_7": [
+ "randomMaterial_7",
+ "randomMaterial_1",
+ "罗德岛物资补给IV",
+ "CONSUME",
+ 60107
+ ],
+ "罗德岛物资补给IV": [
+ "randomMaterial_7",
+ "randomMaterial_1",
+ "罗德岛物资补给IV",
+ "CONSUME",
+ 60107
+ ],
+ "randomMaterial_8": [
+ "randomMaterial_8",
+ "randomMaterial_1",
+ "罗德岛物资补给V",
+ "CONSUME",
+ 60108
+ ],
+ "罗德岛物资补给V": [
+ "randomMaterial_8",
+ "randomMaterial_1",
+ "罗德岛物资补给V",
+ "CONSUME",
+ 60108
+ ],
+ "randomMaterial_9": [
+ "randomMaterial_9",
+ "randomMaterial_1",
+ "罗德岛物资补给VI",
+ "CONSUME",
+ 60109
+ ],
+ "罗德岛物资补给VI": [
+ "randomMaterial_9",
+ "randomMaterial_1",
+ "罗德岛物资补给VI",
+ "CONSUME",
+ 60109
+ ],
+ "randomMaterial_10": [
+ "randomMaterial_10",
+ "randomMaterial_1",
+ "罗德岛物资补给VII",
+ "CONSUME",
+ 60110
+ ],
+ "罗德岛物资补给VII": [
+ "randomMaterial_10",
+ "randomMaterial_1",
+ "罗德岛物资补给VII",
+ "CONSUME",
+ 60110
+ ],
+ "randomMaterial_rhine2": [
+ "randomMaterial_rhine2",
+ "randomMaterial_rhine2",
+ "技术调查补给",
+ "CONSUME",
+ 60111
+ ],
+ "技术调查补给": [
+ "randomMaterial_rhine2",
+ "randomMaterial_rhine2",
+ "技术调查补给",
+ "CONSUME",
+ 60111
+ ],
+ "randomDiamondShd_1": [
+ "randomDiamondShd_1",
+ "randomDiamondShd_1",
+ "罗德岛迎春红包",
+ "CONSUME",
+ 60111
+ ],
+ "罗德岛迎春红包": [
+ "randomDiamondShd_1",
+ "randomDiamondShd_1",
+ "罗德岛迎春红包",
+ "CONSUME",
+ 60111
+ ],
+ "randomDiamondShd_2": [
+ "randomDiamondShd_2",
+ "randomDiamondShd_2",
+ "庆典礼盒",
+ "CONSUME",
+ 60112
+ ],
+ "庆典礼盒": [
+ "randomDiamondShd_2",
+ "randomDiamondShd_2",
+ "庆典礼盒",
+ "CONSUME",
+ 60112
+ ],
+ "randomMaterial_siesta2": [
+ "randomMaterial_siesta2",
+ "randomMaterial_siesta2",
+ "峯联贸易物流补给",
+ "CONSUME",
+ 60113
+ ],
+ "峯联贸易物流补给": [
+ "randomMaterial_siesta2",
+ "randomMaterial_siesta2",
+ "峯联贸易物流补给",
+ "CONSUME",
+ 60113
+ ],
+ "randomMaterial_leith2": [
+ "randomMaterial_leith2",
+ "randomMaterial_leith2",
+ "查访补给",
+ "CONSUME",
+ 60114
+ ],
+ "查访补给": [
+ "randomMaterial_leith2",
+ "randomMaterial_leith2",
+ "查访补给",
+ "CONSUME",
+ 60114
+ ],
+ "2001": [
+ "2001",
+ "sprite_exp_card_t1",
+ "基础作战记录",
+ "MATERIAL",
+ 70004
+ ],
+ "基础作战记录": [
+ "2001",
+ "sprite_exp_card_t1",
+ "基础作战记录",
+ "MATERIAL",
+ 70004
+ ],
+ "2002": [
+ "2002",
+ "sprite_exp_card_t2",
+ "初级作战记录",
+ "MATERIAL",
+ 70003
+ ],
+ "初级作战记录": [
+ "2002",
+ "sprite_exp_card_t2",
+ "初级作战记录",
+ "MATERIAL",
+ 70003
+ ],
+ "2003": [
+ "2003",
+ "sprite_exp_card_t3",
+ "中级作战记录",
+ "MATERIAL",
+ 70002
+ ],
+ "中级作战记录": [
+ "2003",
+ "sprite_exp_card_t3",
+ "中级作战记录",
+ "MATERIAL",
+ 70002
+ ],
+ "2004": [
+ "2004",
+ "sprite_exp_card_t4",
+ "高级作战记录",
+ "MATERIAL",
+ 70001
+ ],
+ "高级作战记录": [
+ "2004",
+ "sprite_exp_card_t4",
+ "高级作战记录",
+ "MATERIAL",
+ 70001
+ ],
+ "3301": [
+ "3301",
+ "MTL_SKILL1",
+ "技巧概要·卷1",
+ "MATERIAL",
+ 80003
+ ],
+ "技巧概要·卷1": [
+ "3301",
+ "MTL_SKILL1",
+ "技巧概要·卷1",
+ "MATERIAL",
+ 80003
+ ],
+ "3302": [
+ "3302",
+ "MTL_SKILL2",
+ "技巧概要·卷2",
+ "MATERIAL",
+ 80002
+ ],
+ "技巧概要·卷2": [
+ "3302",
+ "MTL_SKILL2",
+ "技巧概要·卷2",
+ "MATERIAL",
+ 80002
+ ],
+ "3303": [
+ "3303",
+ "MTL_SKILL3",
+ "技巧概要·卷3",
+ "MATERIAL",
+ 80001
+ ],
+ "技巧概要·卷3": [
+ "3303",
+ "MTL_SKILL3",
+ "技巧概要·卷3",
+ "MATERIAL",
+ 80001
+ ],
+ "mod_unlock_token": [
+ "mod_unlock_token",
+ "mod_unlock_token",
+ "模组数据块",
+ "MATERIAL",
+ 90001
+ ],
+ "模组数据块": [
+ "mod_unlock_token",
+ "mod_unlock_token",
+ "模组数据块",
+ "MATERIAL",
+ 90001
+ ],
+ "mod_update_token_2": [
+ "mod_update_token_2",
+ "mod_update_token_2",
+ "数据增补仪",
+ "MATERIAL",
+ 90002
+ ],
+ "数据增补仪": [
+ "mod_update_token_2",
+ "mod_update_token_2",
+ "数据增补仪",
+ "MATERIAL",
+ 90002
+ ],
+ "mod_update_token_1": [
+ "mod_update_token_1",
+ "mod_update_token_1",
+ "数据增补条",
+ "MATERIAL",
+ 90003
+ ],
+ "数据增补条": [
+ "mod_update_token_1",
+ "mod_update_token_1",
+ "数据增补条",
+ "MATERIAL",
+ 90003
+ ],
+ "30051": [
+ "30051",
+ "MTL_SL_KETONE1",
+ "双酮",
+ "MATERIAL",
+ 100053
+ ],
+ "双酮": [
+ "30051",
+ "MTL_SL_KETONE1",
+ "双酮",
+ "MATERIAL",
+ 100053
+ ],
+ "30052": [
+ "30052",
+ "MTL_SL_KETONE2",
+ "酮凝集",
+ "MATERIAL",
+ 100052
+ ],
+ "酮凝集": [
+ "30052",
+ "MTL_SL_KETONE2",
+ "酮凝集",
+ "MATERIAL",
+ 100052
+ ],
+ "30053": [
+ "30053",
+ "MTL_SL_KETONE3",
+ "酮凝集组",
+ "MATERIAL",
+ 100051
+ ],
+ "酮凝集组": [
+ "30053",
+ "MTL_SL_KETONE3",
+ "酮凝集组",
+ "MATERIAL",
+ 100051
+ ],
+ "30054": [
+ "30054",
+ "MTL_SL_KETONE4",
+ "酮阵列",
+ "MATERIAL",
+ 100050
+ ],
+ "酮阵列": [
+ "30054",
+ "MTL_SL_KETONE4",
+ "酮阵列",
+ "MATERIAL",
+ 100050
+ ],
+ "30041": [
+ "30041",
+ "MTL_SL_IRON1",
+ "异铁碎片",
+ "MATERIAL",
+ 100049
+ ],
+ "异铁碎片": [
+ "30041",
+ "MTL_SL_IRON1",
+ "异铁碎片",
+ "MATERIAL",
+ 100049
+ ],
+ "30042": [
+ "30042",
+ "MTL_SL_IRON2",
+ "异铁",
+ "MATERIAL",
+ 100048
+ ],
+ "异铁": [
+ "30042",
+ "MTL_SL_IRON2",
+ "异铁",
+ "MATERIAL",
+ 100048
+ ],
+ "30043": [
+ "30043",
+ "MTL_SL_IRON3",
+ "异铁组",
+ "MATERIAL",
+ 100047
+ ],
+ "异铁组": [
+ "30043",
+ "MTL_SL_IRON3",
+ "异铁组",
+ "MATERIAL",
+ 100047
+ ],
+ "30044": [
+ "30044",
+ "MTL_SL_IRON4",
+ "异铁块",
+ "MATERIAL",
+ 100046
+ ],
+ "异铁块": [
+ "30044",
+ "MTL_SL_IRON4",
+ "异铁块",
+ "MATERIAL",
+ 100046
+ ],
+ "30021": [
+ "30021",
+ "MTL_SL_STRG1",
+ "代糖",
+ "MATERIAL",
+ 100045
+ ],
+ "代糖": [
+ "30021",
+ "MTL_SL_STRG1",
+ "代糖",
+ "MATERIAL",
+ 100045
+ ],
+ "30022": [
+ "30022",
+ "MTL_SL_STRG2",
+ "糖",
+ "MATERIAL",
+ 100044
+ ],
+ "糖": [
+ "30022",
+ "MTL_SL_STRG2",
+ "糖",
+ "MATERIAL",
+ 100044
+ ],
+ "30023": [
+ "30023",
+ "MTL_SL_STRG3",
+ "糖组",
+ "MATERIAL",
+ 100043
+ ],
+ "糖组": [
+ "30023",
+ "MTL_SL_STRG3",
+ "糖组",
+ "MATERIAL",
+ 100043
+ ],
+ "30024": [
+ "30024",
+ "MTL_SL_STRG4",
+ "糖聚块",
+ "MATERIAL",
+ 100042
+ ],
+ "糖聚块": [
+ "30024",
+ "MTL_SL_STRG4",
+ "糖聚块",
+ "MATERIAL",
+ 100042
+ ],
+ "30031": [
+ "30031",
+ "MTL_SL_RUSH1",
+ "酯原料",
+ "MATERIAL",
+ 100041
+ ],
+ "酯原料": [
+ "30031",
+ "MTL_SL_RUSH1",
+ "酯原料",
+ "MATERIAL",
+ 100041
+ ],
+ "30032": [
+ "30032",
+ "MTL_SL_RUSH2",
+ "聚酸酯",
+ "MATERIAL",
+ 100040
+ ],
+ "聚酸酯": [
+ "30032",
+ "MTL_SL_RUSH2",
+ "聚酸酯",
+ "MATERIAL",
+ 100040
+ ],
+ "30033": [
+ "30033",
+ "MTL_SL_RUSH3",
+ "聚酸酯组",
+ "MATERIAL",
+ 100039
+ ],
+ "聚酸酯组": [
+ "30033",
+ "MTL_SL_RUSH3",
+ "聚酸酯组",
+ "MATERIAL",
+ 100039
+ ],
+ "30034": [
+ "30034",
+ "MTL_SL_RUSH4",
+ "聚酸酯块",
+ "MATERIAL",
+ 100038
+ ],
+ "聚酸酯块": [
+ "30034",
+ "MTL_SL_RUSH4",
+ "聚酸酯块",
+ "MATERIAL",
+ 100038
+ ],
+ "30061": [
+ "30061",
+ "MTL_SL_BOSS1",
+ "破损装置",
+ "MATERIAL",
+ 100037
+ ],
+ "破损装置": [
+ "30061",
+ "MTL_SL_BOSS1",
+ "破损装置",
+ "MATERIAL",
+ 100037
+ ],
+ "30062": [
+ "30062",
+ "MTL_SL_BOSS2",
+ "装置",
+ "MATERIAL",
+ 100036
+ ],
+ "装置": [
+ "30062",
+ "MTL_SL_BOSS2",
+ "装置",
+ "MATERIAL",
+ 100036
+ ],
+ "30063": [
+ "30063",
+ "MTL_SL_BOSS3",
+ "全新装置",
+ "MATERIAL",
+ 100035
+ ],
+ "全新装置": [
+ "30063",
+ "MTL_SL_BOSS3",
+ "全新装置",
+ "MATERIAL",
+ 100035
+ ],
+ "30064": [
+ "30064",
+ "MTL_SL_BOSS4",
+ "改量装置",
+ "MATERIAL",
+ 100034
+ ],
+ "改量装置": [
+ "30064",
+ "MTL_SL_BOSS4",
+ "改量装置",
+ "MATERIAL",
+ 100034
+ ],
+ "30011": [
+ "30011",
+ "MTL_SL_G1",
+ "源岩",
+ "MATERIAL",
+ 100033
+ ],
+ "源岩": [
+ "30011",
+ "MTL_SL_G1",
+ "源岩",
+ "MATERIAL",
+ 100033
+ ],
+ "30012": [
+ "30012",
+ "MTL_SL_G2",
+ "固源岩",
+ "MATERIAL",
+ 100032
+ ],
+ "固源岩": [
+ "30012",
+ "MTL_SL_G2",
+ "固源岩",
+ "MATERIAL",
+ 100032
+ ],
+ "30013": [
+ "30013",
+ "MTL_SL_G3",
+ "固源岩组",
+ "MATERIAL",
+ 100031
+ ],
+ "固源岩组": [
+ "30013",
+ "MTL_SL_G3",
+ "固源岩组",
+ "MATERIAL",
+ 100031
+ ],
+ "30014": [
+ "30014",
+ "MTL_SL_G4",
+ "提纯源岩",
+ "MATERIAL",
+ 100030
+ ],
+ "提纯源岩": [
+ "30014",
+ "MTL_SL_G4",
+ "提纯源岩",
+ "MATERIAL",
+ 100030
+ ],
+ "30103": [
+ "30103",
+ "MTL_SL_RMA7012",
+ "RMA70-12",
+ "MATERIAL",
+ 100029
+ ],
+ "RMA70-12": [
+ "30103",
+ "MTL_SL_RMA7012",
+ "RMA70-12",
+ "MATERIAL",
+ 100029
+ ],
+ "30104": [
+ "30104",
+ "MTL_SL_RMA7024",
+ "RMA70-24",
+ "MATERIAL",
+ 100028
+ ],
+ "RMA70-24": [
+ "30104",
+ "MTL_SL_RMA7024",
+ "RMA70-24",
+ "MATERIAL",
+ 100028
+ ],
+ "30093": [
+ "30093",
+ "MTL_SL_PG1",
+ "研磨石",
+ "MATERIAL",
+ 100027
+ ],
+ "研磨石": [
+ "30093",
+ "MTL_SL_PG1",
+ "研磨石",
+ "MATERIAL",
+ 100027
+ ],
+ "30094": [
+ "30094",
+ "MTL_SL_PG2",
+ "五水研磨石",
+ "MATERIAL",
+ 100026
+ ],
+ "五水研磨石": [
+ "30094",
+ "MTL_SL_PG2",
+ "五水研磨石",
+ "MATERIAL",
+ 100026
+ ],
+ "30083": [
+ "30083",
+ "MTL_SL_MANGANESE1",
+ "轻锰矿",
+ "MATERIAL",
+ 100025
+ ],
+ "轻锰矿": [
+ "30083",
+ "MTL_SL_MANGANESE1",
+ "轻锰矿",
+ "MATERIAL",
+ 100025
+ ],
+ "30084": [
+ "30084",
+ "MTL_SL_MANGANESE2",
+ "三水锰矿",
+ "MATERIAL",
+ 100024
+ ],
+ "三水锰矿": [
+ "30084",
+ "MTL_SL_MANGANESE2",
+ "三水锰矿",
+ "MATERIAL",
+ 100024
+ ],
+ "30073": [
+ "30073",
+ "MTL_SL_ALCOHOL1",
+ "扭转醇",
+ "MATERIAL",
+ 100023
+ ],
+ "扭转醇": [
+ "30073",
+ "MTL_SL_ALCOHOL1",
+ "扭转醇",
+ "MATERIAL",
+ 100023
+ ],
+ "30074": [
+ "30074",
+ "MTL_SL_ALCOHOL2",
+ "白马醇",
+ "MATERIAL",
+ 100022
+ ],
+ "白马醇": [
+ "30074",
+ "MTL_SL_ALCOHOL2",
+ "白马醇",
+ "MATERIAL",
+ 100022
+ ],
+ "31013": [
+ "31013",
+ "MTL_SL_PGEL3",
+ "凝胶",
+ "MATERIAL",
+ 100021
+ ],
+ "凝胶": [
+ "31013",
+ "MTL_SL_PGEL3",
+ "凝胶",
+ "MATERIAL",
+ 100021
+ ],
+ "31014": [
+ "31014",
+ "MTL_SL_PGEL4",
+ "聚合凝胶",
+ "MATERIAL",
+ 100020
+ ],
+ "聚合凝胶": [
+ "31014",
+ "MTL_SL_PGEL4",
+ "聚合凝胶",
+ "MATERIAL",
+ 100020
+ ],
+ "31023": [
+ "31023",
+ "MTL_SL_IAM3",
+ "炽合金",
+ "MATERIAL",
+ 100019
+ ],
+ "炽合金": [
+ "31023",
+ "MTL_SL_IAM3",
+ "炽合金",
+ "MATERIAL",
+ 100019
+ ],
+ "31024": [
+ "31024",
+ "MTL_SL_IAM4",
+ "炽合金块",
+ "MATERIAL",
+ 100018
+ ],
+ "炽合金块": [
+ "31024",
+ "MTL_SL_IAM4",
+ "炽合金块",
+ "MATERIAL",
+ 100018
+ ],
+ "31033": [
+ "31033",
+ "MTL_SL_OC3",
+ "晶体元件",
+ "MATERIAL",
+ 100017
+ ],
+ "晶体元件": [
+ "31033",
+ "MTL_SL_OC3",
+ "晶体元件",
+ "MATERIAL",
+ 100017
+ ],
+ "31034": [
+ "31034",
+ "MTL_SL_OC4",
+ "晶体电路",
+ "MATERIAL",
+ 100016
+ ],
+ "晶体电路": [
+ "31034",
+ "MTL_SL_OC4",
+ "晶体电路",
+ "MATERIAL",
+ 100016
+ ],
+ "31043": [
+ "31043",
+ "MTL_SL_SS",
+ "半自然溶剂",
+ "MATERIAL",
+ 100015
+ ],
+ "半自然溶剂": [
+ "31043",
+ "MTL_SL_SS",
+ "半自然溶剂",
+ "MATERIAL",
+ 100015
+ ],
+ "31044": [
+ "31044",
+ "MTL_SL_RS",
+ "精炼溶剂",
+ "MATERIAL",
+ 100014
+ ],
+ "精炼溶剂": [
+ "31044",
+ "MTL_SL_RS",
+ "精炼溶剂",
+ "MATERIAL",
+ 100014
+ ],
+ "31053": [
+ "31053",
+ "MTL_SL_CCF",
+ "化合切削液",
+ "MATERIAL",
+ 100013
+ ],
+ "化合切削液": [
+ "31053",
+ "MTL_SL_CCF",
+ "化合切削液",
+ "MATERIAL",
+ 100013
+ ],
+ "31054": [
+ "31054",
+ "MTL_SL_PLCF",
+ "切削原液",
+ "MATERIAL",
+ 100012
+ ],
+ "切削原液": [
+ "31054",
+ "MTL_SL_PLCF",
+ "切削原液",
+ "MATERIAL",
+ 100012
+ ],
+ "31063": [
+ "31063",
+ "MTL_SL_ZY",
+ "转质盐组",
+ "MATERIAL",
+ 100011
+ ],
+ "转质盐组": [
+ "31063",
+ "MTL_SL_ZY",
+ "转质盐组",
+ "MATERIAL",
+ 100011
+ ],
+ "31064": [
+ "31064",
+ "MTL_SL_ZYK",
+ "转质盐聚块",
+ "MATERIAL",
+ 100010
+ ],
+ "转质盐聚块": [
+ "31064",
+ "MTL_SL_ZYK",
+ "转质盐聚块",
+ "MATERIAL",
+ 100010
+ ],
+ "31073": [
+ "31073",
+ "MTL_SL_XW",
+ "褐素纤维",
+ "MATERIAL",
+ 100009
+ ],
+ "褐素纤维": [
+ "31073",
+ "MTL_SL_XW",
+ "褐素纤维",
+ "MATERIAL",
+ 100009
+ ],
+ "31074": [
+ "31074",
+ "MTL_SL_XWB",
+ "固化纤维板",
+ "MATERIAL",
+ 100008
+ ],
+ "固化纤维板": [
+ "31074",
+ "MTL_SL_XWB",
+ "固化纤维板",
+ "MATERIAL",
+ 100008
+ ],
+ "31083": [
+ "31083",
+ "MTL_SL_HT",
+ "环烃聚质",
+ "MATERIAL",
+ 100007
+ ],
+ "环烃聚质": [
+ "31083",
+ "MTL_SL_HT",
+ "环烃聚质",
+ "MATERIAL",
+ 100007
+ ],
+ "31084": [
+ "31084",
+ "MTL_SL_HTT",
+ "环烃预制体",
+ "MATERIAL",
+ 100006
+ ],
+ "环烃预制体": [
+ "31084",
+ "MTL_SL_HTT",
+ "环烃预制体",
+ "MATERIAL",
+ 100006
+ ],
+ "30115": [
+ "30115",
+ "MTL_SL_PP",
+ "聚合剂",
+ "MATERIAL",
+ 100005
+ ],
+ "聚合剂": [
+ "30115",
+ "MTL_SL_PP",
+ "聚合剂",
+ "MATERIAL",
+ 100005
+ ],
+ "30125": [
+ "30125",
+ "MTL_SL_BN",
+ "双极纳米片",
+ "MATERIAL",
+ 100004
+ ],
+ "双极纳米片": [
+ "30125",
+ "MTL_SL_BN",
+ "双极纳米片",
+ "MATERIAL",
+ 100004
+ ],
+ "30135": [
+ "30135",
+ "MTL_SL_DS",
+ "D32钢",
+ "MATERIAL",
+ 100003
+ ],
+ "D32钢": [
+ "30135",
+ "MTL_SL_DS",
+ "D32钢",
+ "MATERIAL",
+ 100003
+ ],
+ "30145": [
+ "30145",
+ "MTL_SL_OEU",
+ "晶体电子单元",
+ "MATERIAL",
+ 100002
+ ],
+ "晶体电子单元": [
+ "30145",
+ "MTL_SL_OEU",
+ "晶体电子单元",
+ "MATERIAL",
+ 100002
+ ],
+ "30155": [
+ "30155",
+ "MTL_SL_SHJ",
+ "烧结核凝晶",
+ "MATERIAL",
+ 100001
+ ],
+ "烧结核凝晶": [
+ "30155",
+ "MTL_SL_SHJ",
+ "烧结核凝晶",
+ "MATERIAL",
+ 100001
+ ],
+ "3105": [
+ "3105",
+ "MTL_BASE_ESS",
+ "龙骨",
+ "NORMAL",
+ 200001
+ ],
+ "龙骨": [
+ "3105",
+ "MTL_BASE_ESS",
+ "龙骨",
+ "NORMAL",
+ 200001
+ ],
+ "3401": [
+ "3401",
+ "COIN_FURN",
+ "家具零件",
+ "NORMAL",
+ 200002
+ ],
+ "家具零件": [
+ "3401",
+ "COIN_FURN",
+ "家具零件",
+ "NORMAL",
+ 200002
+ ],
+ "3131": [
+ "3131",
+ "MTL_BASE_SYNTH1",
+ "基础加固建材",
+ "NORMAL",
+ 200005
+ ],
+ "基础加固建材": [
+ "3131",
+ "MTL_BASE_SYNTH1",
+ "基础加固建材",
+ "NORMAL",
+ 200005
+ ],
+ "3132": [
+ "3132",
+ "MTL_BASE_SYNTH2",
+ "进阶加固建材",
+ "NORMAL",
+ 200004
+ ],
+ "进阶加固建材": [
+ "3132",
+ "MTL_BASE_SYNTH2",
+ "进阶加固建材",
+ "NORMAL",
+ 200004
+ ],
+ "3133": [
+ "3133",
+ "MTL_BASE_SYNTH3",
+ "高级加固建材",
+ "NORMAL",
+ 200003
+ ],
+ "高级加固建材": [
+ "3133",
+ "MTL_BASE_SYNTH3",
+ "高级加固建材",
+ "NORMAL",
+ 200003
+ ],
+ "3112": [
+ "3112",
+ "MTL_BASE_SL1",
+ "碳",
+ "NORMAL",
+ 200008
+ ],
+ "碳": [
+ "3112",
+ "MTL_BASE_SL1",
+ "碳",
+ "NORMAL",
+ 200008
+ ],
+ "3113": [
+ "3113",
+ "MTL_BASE_SL2",
+ "碳素",
+ "NORMAL",
+ 200007
+ ],
+ "碳素": [
+ "3113",
+ "MTL_BASE_SL2",
+ "碳素",
+ "NORMAL",
+ 200007
+ ],
+ "3114": [
+ "3114",
+ "MTL_BASE_SL3",
+ "碳素组",
+ "NORMAL",
+ 200006
+ ],
+ "碳素组": [
+ "3114",
+ "MTL_BASE_SL3",
+ "碳素组",
+ "NORMAL",
+ 200006
+ ],
+ "32001": [
+ "32001",
+ "MTL_ASC_DI",
+ "芯片助剂",
+ "MATERIAL",
+ 400001
+ ],
+ "芯片助剂": [
+ "32001",
+ "MTL_ASC_DI",
+ "芯片助剂",
+ "MATERIAL",
+ 400001
+ ],
+ "3213": [
+ "3213",
+ "MTL_ASC_PIO3",
+ "先锋双芯片",
+ "MATERIAL",
+ 400002
+ ],
+ "先锋双芯片": [
+ "3213",
+ "MTL_ASC_PIO3",
+ "先锋双芯片",
+ "MATERIAL",
+ 400002
+ ],
+ "3223": [
+ "3223",
+ "MTL_ASC_GRD3",
+ "近卫双芯片",
+ "MATERIAL",
+ 400003
+ ],
+ "近卫双芯片": [
+ "3223",
+ "MTL_ASC_GRD3",
+ "近卫双芯片",
+ "MATERIAL",
+ 400003
+ ],
+ "3233": [
+ "3233",
+ "MTL_ASC_TNK3",
+ "重装双芯片",
+ "MATERIAL",
+ 400004
+ ],
+ "重装双芯片": [
+ "3233",
+ "MTL_ASC_TNK3",
+ "重装双芯片",
+ "MATERIAL",
+ 400004
+ ],
+ "3243": [
+ "3243",
+ "MTL_ASC_SNP3",
+ "狙击双芯片",
+ "MATERIAL",
+ 400005
+ ],
+ "狙击双芯片": [
+ "3243",
+ "MTL_ASC_SNP3",
+ "狙击双芯片",
+ "MATERIAL",
+ 400005
+ ],
+ "3253": [
+ "3253",
+ "MTL_ASC_CST3",
+ "术师双芯片",
+ "MATERIAL",
+ 400006
+ ],
+ "术师双芯片": [
+ "3253",
+ "MTL_ASC_CST3",
+ "术师双芯片",
+ "MATERIAL",
+ 400006
+ ],
+ "3263": [
+ "3263",
+ "MTL_ASC_MED3",
+ "医疗双芯片",
+ "MATERIAL",
+ 400007
+ ],
+ "医疗双芯片": [
+ "3263",
+ "MTL_ASC_MED3",
+ "医疗双芯片",
+ "MATERIAL",
+ 400007
+ ],
+ "3273": [
+ "3273",
+ "MTL_ASC_SUP3",
+ "辅助双芯片",
+ "MATERIAL",
+ 400008
+ ],
+ "辅助双芯片": [
+ "3273",
+ "MTL_ASC_SUP3",
+ "辅助双芯片",
+ "MATERIAL",
+ 400008
+ ],
+ "3283": [
+ "3283",
+ "MTL_ASC_SPC3",
+ "特种双芯片",
+ "MATERIAL",
+ 400009
+ ],
+ "特种双芯片": [
+ "3283",
+ "MTL_ASC_SPC3",
+ "特种双芯片",
+ "MATERIAL",
+ 400009
+ ],
+ "3212": [
+ "3212",
+ "MTL_ASC_PIO2",
+ "先锋芯片组",
+ "MATERIAL",
+ 400010
+ ],
+ "先锋芯片组": [
+ "3212",
+ "MTL_ASC_PIO2",
+ "先锋芯片组",
+ "MATERIAL",
+ 400010
+ ],
+ "3222": [
+ "3222",
+ "MTL_ASC_GRD2",
+ "近卫芯片组",
+ "MATERIAL",
+ 400011
+ ],
+ "近卫芯片组": [
+ "3222",
+ "MTL_ASC_GRD2",
+ "近卫芯片组",
+ "MATERIAL",
+ 400011
+ ],
+ "3232": [
+ "3232",
+ "MTL_ASC_TNK2",
+ "重装芯片组",
+ "MATERIAL",
+ 400012
+ ],
+ "重装芯片组": [
+ "3232",
+ "MTL_ASC_TNK2",
+ "重装芯片组",
+ "MATERIAL",
+ 400012
+ ],
+ "3242": [
+ "3242",
+ "MTL_ASC_SNP2",
+ "狙击芯片组",
+ "MATERIAL",
+ 400013
+ ],
+ "狙击芯片组": [
+ "3242",
+ "MTL_ASC_SNP2",
+ "狙击芯片组",
+ "MATERIAL",
+ 400013
+ ],
+ "3252": [
+ "3252",
+ "MTL_ASC_CST2",
+ "术师芯片组",
+ "MATERIAL",
+ 400014
+ ],
+ "术师芯片组": [
+ "3252",
+ "MTL_ASC_CST2",
+ "术师芯片组",
+ "MATERIAL",
+ 400014
+ ],
+ "3262": [
+ "3262",
+ "MTL_ASC_MED2",
+ "医疗芯片组",
+ "MATERIAL",
+ 400015
+ ],
+ "医疗芯片组": [
+ "3262",
+ "MTL_ASC_MED2",
+ "医疗芯片组",
+ "MATERIAL",
+ 400015
+ ],
+ "3272": [
+ "3272",
+ "MTL_ASC_SUP2",
+ "辅助芯片组",
+ "MATERIAL",
+ 400016
+ ],
+ "辅助芯片组": [
+ "3272",
+ "MTL_ASC_SUP2",
+ "辅助芯片组",
+ "MATERIAL",
+ 400016
+ ],
+ "3282": [
+ "3282",
+ "MTL_ASC_SPC2",
+ "特种芯片组",
+ "MATERIAL",
+ 400017
+ ],
+ "特种芯片组": [
+ "3282",
+ "MTL_ASC_SPC2",
+ "特种芯片组",
+ "MATERIAL",
+ 400017
+ ],
+ "3211": [
+ "3211",
+ "MTL_ASC_PIO1",
+ "先锋芯片",
+ "MATERIAL",
+ 400018
+ ],
+ "先锋芯片": [
+ "3211",
+ "MTL_ASC_PIO1",
+ "先锋芯片",
+ "MATERIAL",
+ 400018
+ ],
+ "3221": [
+ "3221",
+ "MTL_ASC_GRD1",
+ "近卫芯片",
+ "MATERIAL",
+ 400019
+ ],
+ "近卫芯片": [
+ "3221",
+ "MTL_ASC_GRD1",
+ "近卫芯片",
+ "MATERIAL",
+ 400019
+ ],
+ "3231": [
+ "3231",
+ "MTL_ASC_TNK1",
+ "重装芯片",
+ "MATERIAL",
+ 400020
+ ],
+ "重装芯片": [
+ "3231",
+ "MTL_ASC_TNK1",
+ "重装芯片",
+ "MATERIAL",
+ 400020
+ ],
+ "3241": [
+ "3241",
+ "MTL_ASC_SNP1",
+ "狙击芯片",
+ "MATERIAL",
+ 400021
+ ],
+ "狙击芯片": [
+ "3241",
+ "MTL_ASC_SNP1",
+ "狙击芯片",
+ "MATERIAL",
+ 400021
+ ],
+ "3251": [
+ "3251",
+ "MTL_ASC_CST1",
+ "术师芯片",
+ "MATERIAL",
+ 400022
+ ],
+ "术师芯片": [
+ "3251",
+ "MTL_ASC_CST1",
+ "术师芯片",
+ "MATERIAL",
+ 400022
+ ],
+ "3261": [
+ "3261",
+ "MTL_ASC_MED1",
+ "医疗芯片",
+ "MATERIAL",
+ 400023
+ ],
+ "医疗芯片": [
+ "3261",
+ "MTL_ASC_MED1",
+ "医疗芯片",
+ "MATERIAL",
+ 400023
+ ],
+ "3271": [
+ "3271",
+ "MTL_ASC_SUP1",
+ "辅助芯片",
+ "MATERIAL",
+ 400024
+ ],
+ "辅助芯片": [
+ "3271",
+ "MTL_ASC_SUP1",
+ "辅助芯片",
+ "MATERIAL",
+ 400024
+ ],
+ "3281": [
+ "3281",
+ "MTL_ASC_SPC1",
+ "特种芯片",
+ "MATERIAL",
+ 400025
+ ],
+ "特种芯片": [
+ "3281",
+ "MTL_ASC_SPC1",
+ "特种芯片",
+ "MATERIAL",
+ 400025
+ ],
+ "tier1_pioneer": [
+ "tier1_pioneer",
+ "tier1_pioneer",
+ "先锋信物复制品",
+ "MATERIAL",
+ 450041
+ ],
+ "先锋信物复制品": [
+ "tier1_pioneer",
+ "tier1_pioneer",
+ "先锋信物复制品",
+ "MATERIAL",
+ 450041
+ ],
+ "tier1_guard": [
+ "tier1_guard",
+ "tier1_guard",
+ "近卫信物复制品",
+ "MATERIAL",
+ 450042
+ ],
+ "近卫信物复制品": [
+ "tier1_guard",
+ "tier1_guard",
+ "近卫信物复制品",
+ "MATERIAL",
+ 450042
+ ],
+ "tier1_tank": [
+ "tier1_tank",
+ "tier1_tank",
+ "重装信物复制品",
+ "MATERIAL",
+ 450043
+ ],
+ "重装信物复制品": [
+ "tier1_tank",
+ "tier1_tank",
+ "重装信物复制品",
+ "MATERIAL",
+ 450043
+ ],
+ "tier1_sniper": [
+ "tier1_sniper",
+ "tier1_sniper",
+ "狙击信物复制品",
+ "MATERIAL",
+ 450044
+ ],
+ "狙击信物复制品": [
+ "tier1_sniper",
+ "tier1_sniper",
+ "狙击信物复制品",
+ "MATERIAL",
+ 450044
+ ],
+ "tier1_caster": [
+ "tier1_caster",
+ "tier1_caster",
+ "术师信物复制品",
+ "MATERIAL",
+ 450045
+ ],
+ "术师信物复制品": [
+ "tier1_caster",
+ "tier1_caster",
+ "术师信物复制品",
+ "MATERIAL",
+ 450045
+ ],
+ "tier1_medic": [
+ "tier1_medic",
+ "tier1_medic",
+ "医疗信物复制品",
+ "MATERIAL",
+ 450046
+ ],
+ "医疗信物复制品": [
+ "tier1_medic",
+ "tier1_medic",
+ "医疗信物复制品",
+ "MATERIAL",
+ 450046
+ ],
+ "tier1_supporter": [
+ "tier1_supporter",
+ "tier1_supporter",
+ "辅助信物复制品",
+ "MATERIAL",
+ 450047
+ ],
+ "辅助信物复制品": [
+ "tier1_supporter",
+ "tier1_supporter",
+ "辅助信物复制品",
+ "MATERIAL",
+ 450047
+ ],
+ "tier1_special": [
+ "tier1_special",
+ "tier1_special",
+ "特种信物复制品",
+ "MATERIAL",
+ 450048
+ ],
+ "特种信物复制品": [
+ "tier1_special",
+ "tier1_special",
+ "特种信物复制品",
+ "MATERIAL",
+ 450048
+ ],
+ "tier2_pioneer": [
+ "tier2_pioneer",
+ "tier2_pioneer",
+ "先锋信物原件",
+ "MATERIAL",
+ 450033
+ ],
+ "先锋信物原件": [
+ "tier2_pioneer",
+ "tier2_pioneer",
+ "先锋信物原件",
+ "MATERIAL",
+ 450033
+ ],
+ "tier2_guard": [
+ "tier2_guard",
+ "tier2_guard",
+ "近卫信物原件",
+ "MATERIAL",
+ 450034
+ ],
+ "近卫信物原件": [
+ "tier2_guard",
+ "tier2_guard",
+ "近卫信物原件",
+ "MATERIAL",
+ 450034
+ ],
+ "tier2_tank": [
+ "tier2_tank",
+ "tier2_tank",
+ "重装信物原件",
+ "MATERIAL",
+ 450035
+ ],
+ "重装信物原件": [
+ "tier2_tank",
+ "tier2_tank",
+ "重装信物原件",
+ "MATERIAL",
+ 450035
+ ],
+ "tier2_sniper": [
+ "tier2_sniper",
+ "tier2_sniper",
+ "狙击信物原件",
+ "MATERIAL",
+ 450036
+ ],
+ "狙击信物原件": [
+ "tier2_sniper",
+ "tier2_sniper",
+ "狙击信物原件",
+ "MATERIAL",
+ 450036
+ ],
+ "tier2_caster": [
+ "tier2_caster",
+ "tier2_caster",
+ "术师信物原件",
+ "MATERIAL",
+ 450037
+ ],
+ "术师信物原件": [
+ "tier2_caster",
+ "tier2_caster",
+ "术师信物原件",
+ "MATERIAL",
+ 450037
+ ],
+ "tier2_medic": [
+ "tier2_medic",
+ "tier2_medic",
+ "医疗信物原件",
+ "MATERIAL",
+ 450038
+ ],
+ "医疗信物原件": [
+ "tier2_medic",
+ "tier2_medic",
+ "医疗信物原件",
+ "MATERIAL",
+ 450038
+ ],
+ "tier2_supporter": [
+ "tier2_supporter",
+ "tier2_supporter",
+ "辅助信物原件",
+ "MATERIAL",
+ 450039
+ ],
+ "辅助信物原件": [
+ "tier2_supporter",
+ "tier2_supporter",
+ "辅助信物原件",
+ "MATERIAL",
+ 450039
+ ],
+ "tier2_special": [
+ "tier2_special",
+ "tier2_special",
+ "特种信物原件",
+ "MATERIAL",
+ 450040
+ ],
+ "特种信物原件": [
+ "tier2_special",
+ "tier2_special",
+ "特种信物原件",
+ "MATERIAL",
+ 450040
+ ],
+ "tier3_pioneer": [
+ "tier3_pioneer",
+ "tier3_pioneer",
+ "先锋信物藏品",
+ "MATERIAL",
+ 450025
+ ],
+ "先锋信物藏品": [
+ "tier3_pioneer",
+ "tier3_pioneer",
+ "先锋信物藏品",
+ "MATERIAL",
+ 450025
+ ],
+ "tier3_guard": [
+ "tier3_guard",
+ "tier3_guard",
+ "近卫信物藏品",
+ "MATERIAL",
+ 450026
+ ],
+ "近卫信物藏品": [
+ "tier3_guard",
+ "tier3_guard",
+ "近卫信物藏品",
+ "MATERIAL",
+ 450026
+ ],
+ "tier3_tank": [
+ "tier3_tank",
+ "tier3_tank",
+ "重装信物藏品",
+ "MATERIAL",
+ 450027
+ ],
+ "重装信物藏品": [
+ "tier3_tank",
+ "tier3_tank",
+ "重装信物藏品",
+ "MATERIAL",
+ 450027
+ ],
+ "tier3_sniper": [
+ "tier3_sniper",
+ "tier3_sniper",
+ "狙击信物藏品",
+ "MATERIAL",
+ 450028
+ ],
+ "狙击信物藏品": [
+ "tier3_sniper",
+ "tier3_sniper",
+ "狙击信物藏品",
+ "MATERIAL",
+ 450028
+ ],
+ "tier3_caster": [
+ "tier3_caster",
+ "tier3_caster",
+ "术师信物藏品",
+ "MATERIAL",
+ 450029
+ ],
+ "术师信物藏品": [
+ "tier3_caster",
+ "tier3_caster",
+ "术师信物藏品",
+ "MATERIAL",
+ 450029
+ ],
+ "tier3_medic": [
+ "tier3_medic",
+ "tier3_medic",
+ "医疗信物藏品",
+ "MATERIAL",
+ 450030
+ ],
+ "医疗信物藏品": [
+ "tier3_medic",
+ "tier3_medic",
+ "医疗信物藏品",
+ "MATERIAL",
+ 450030
+ ],
+ "tier3_supporter": [
+ "tier3_supporter",
+ "tier3_supporter",
+ "辅助信物藏品",
+ "MATERIAL",
+ 450031
+ ],
+ "辅助信物藏品": [
+ "tier3_supporter",
+ "tier3_supporter",
+ "辅助信物藏品",
+ "MATERIAL",
+ 450031
+ ],
+ "tier3_special": [
+ "tier3_special",
+ "tier3_special",
+ "特种信物藏品",
+ "MATERIAL",
+ 450032
+ ],
+ "特种信物藏品": [
+ "tier3_special",
+ "tier3_special",
+ "特种信物藏品",
+ "MATERIAL",
+ 450032
+ ],
+ "tier4_pioneer": [
+ "tier4_pioneer",
+ "tier4_pioneer",
+ "先锋传承信物",
+ "MATERIAL",
+ 450017
+ ],
+ "先锋传承信物": [
+ "tier4_pioneer",
+ "tier4_pioneer",
+ "先锋传承信物",
+ "MATERIAL",
+ 450017
+ ],
+ "tier4_guard": [
+ "tier4_guard",
+ "tier4_guard",
+ "近卫传承信物",
+ "MATERIAL",
+ 450018
+ ],
+ "近卫传承信物": [
+ "tier4_guard",
+ "tier4_guard",
+ "近卫传承信物",
+ "MATERIAL",
+ 450018
+ ],
+ "tier4_tank": [
+ "tier4_tank",
+ "tier4_tank",
+ "重装传承信物",
+ "MATERIAL",
+ 450019
+ ],
+ "重装传承信物": [
+ "tier4_tank",
+ "tier4_tank",
+ "重装传承信物",
+ "MATERIAL",
+ 450019
+ ],
+ "tier4_sniper": [
+ "tier4_sniper",
+ "tier4_sniper",
+ "狙击传承信物",
+ "MATERIAL",
+ 450020
+ ],
+ "狙击传承信物": [
+ "tier4_sniper",
+ "tier4_sniper",
+ "狙击传承信物",
+ "MATERIAL",
+ 450020
+ ],
+ "tier4_caster": [
+ "tier4_caster",
+ "tier4_caster",
+ "术师传承信物",
+ "MATERIAL",
+ 450021
+ ],
+ "术师传承信物": [
+ "tier4_caster",
+ "tier4_caster",
+ "术师传承信物",
+ "MATERIAL",
+ 450021
+ ],
+ "tier4_medic": [
+ "tier4_medic",
+ "tier4_medic",
+ "医疗传承信物",
+ "MATERIAL",
+ 450022
+ ],
+ "医疗传承信物": [
+ "tier4_medic",
+ "tier4_medic",
+ "医疗传承信物",
+ "MATERIAL",
+ 450022
+ ],
+ "tier4_supporter": [
+ "tier4_supporter",
+ "tier4_supporter",
+ "辅助传承信物",
+ "MATERIAL",
+ 450023
+ ],
+ "辅助传承信物": [
+ "tier4_supporter",
+ "tier4_supporter",
+ "辅助传承信物",
+ "MATERIAL",
+ 450023
+ ],
+ "tier4_special": [
+ "tier4_special",
+ "tier4_special",
+ "特种传承信物",
+ "MATERIAL",
+ 450024
+ ],
+ "特种传承信物": [
+ "tier4_special",
+ "tier4_special",
+ "特种传承信物",
+ "MATERIAL",
+ 450024
+ ],
+ "tier5_pioneer": [
+ "tier5_pioneer",
+ "tier5_pioneer",
+ "先锋遗产信物",
+ "MATERIAL",
+ 450009
+ ],
+ "先锋遗产信物": [
+ "tier5_pioneer",
+ "tier5_pioneer",
+ "先锋遗产信物",
+ "MATERIAL",
+ 450009
+ ],
+ "tier5_guard": [
+ "tier5_guard",
+ "tier5_guard",
+ "近卫遗产信物",
+ "MATERIAL",
+ 450010
+ ],
+ "近卫遗产信物": [
+ "tier5_guard",
+ "tier5_guard",
+ "近卫遗产信物",
+ "MATERIAL",
+ 450010
+ ],
+ "tier5_tank": [
+ "tier5_tank",
+ "tier5_tank",
+ "重装遗产信物",
+ "MATERIAL",
+ 450011
+ ],
+ "重装遗产信物": [
+ "tier5_tank",
+ "tier5_tank",
+ "重装遗产信物",
+ "MATERIAL",
+ 450011
+ ],
+ "tier5_sniper": [
+ "tier5_sniper",
+ "tier5_sniper",
+ "狙击遗产信物",
+ "MATERIAL",
+ 450012
+ ],
+ "狙击遗产信物": [
+ "tier5_sniper",
+ "tier5_sniper",
+ "狙击遗产信物",
+ "MATERIAL",
+ 450012
+ ],
+ "tier5_caster": [
+ "tier5_caster",
+ "tier5_caster",
+ "术师遗产信物",
+ "MATERIAL",
+ 450013
+ ],
+ "术师遗产信物": [
+ "tier5_caster",
+ "tier5_caster",
+ "术师遗产信物",
+ "MATERIAL",
+ 450013
+ ],
+ "tier5_medic": [
+ "tier5_medic",
+ "tier5_medic",
+ "医疗遗产信物",
+ "MATERIAL",
+ 450014
+ ],
+ "医疗遗产信物": [
+ "tier5_medic",
+ "tier5_medic",
+ "医疗遗产信物",
+ "MATERIAL",
+ 450014
+ ],
+ "tier5_supporter": [
+ "tier5_supporter",
+ "tier5_supporter",
+ "辅助遗产信物",
+ "MATERIAL",
+ 450015
+ ],
+ "辅助遗产信物": [
+ "tier5_supporter",
+ "tier5_supporter",
+ "辅助遗产信物",
+ "MATERIAL",
+ 450015
+ ],
+ "tier5_special": [
+ "tier5_special",
+ "tier5_special",
+ "特种遗产信物",
+ "MATERIAL",
+ 450016
+ ],
+ "特种遗产信物": [
+ "tier5_special",
+ "tier5_special",
+ "特种遗产信物",
+ "MATERIAL",
+ 450016
+ ],
+ "tier6_pioneer": [
+ "tier6_pioneer",
+ "tier6_pioneer",
+ "先锋皇家信物",
+ "MATERIAL",
+ 450001
+ ],
+ "先锋皇家信物": [
+ "tier6_pioneer",
+ "tier6_pioneer",
+ "先锋皇家信物",
+ "MATERIAL",
+ 450001
+ ],
+ "tier6_guard": [
+ "tier6_guard",
+ "tier6_guard",
+ "近卫皇家信物",
+ "MATERIAL",
+ 450002
+ ],
+ "近卫皇家信物": [
+ "tier6_guard",
+ "tier6_guard",
+ "近卫皇家信物",
+ "MATERIAL",
+ 450002
+ ],
+ "tier6_tank": [
+ "tier6_tank",
+ "tier6_tank",
+ "重装皇家信物",
+ "MATERIAL",
+ 450003
+ ],
+ "重装皇家信物": [
+ "tier6_tank",
+ "tier6_tank",
+ "重装皇家信物",
+ "MATERIAL",
+ 450003
+ ],
+ "tier6_sniper": [
+ "tier6_sniper",
+ "tier6_sniper",
+ "狙击皇家信物",
+ "MATERIAL",
+ 450004
+ ],
+ "狙击皇家信物": [
+ "tier6_sniper",
+ "tier6_sniper",
+ "狙击皇家信物",
+ "MATERIAL",
+ 450004
+ ],
+ "tier6_caster": [
+ "tier6_caster",
+ "tier6_caster",
+ "术师皇家信物",
+ "MATERIAL",
+ 450005
+ ],
+ "术师皇家信物": [
+ "tier6_caster",
+ "tier6_caster",
+ "术师皇家信物",
+ "MATERIAL",
+ 450005
+ ],
+ "tier6_medic": [
+ "tier6_medic",
+ "tier6_medic",
+ "医疗皇家信物",
+ "MATERIAL",
+ 450006
+ ],
+ "医疗皇家信物": [
+ "tier6_medic",
+ "tier6_medic",
+ "医疗皇家信物",
+ "MATERIAL",
+ 450006
+ ],
+ "tier6_supporter": [
+ "tier6_supporter",
+ "tier6_supporter",
+ "辅助皇家信物",
+ "MATERIAL",
+ 450007
+ ],
+ "辅助皇家信物": [
+ "tier6_supporter",
+ "tier6_supporter",
+ "辅助皇家信物",
+ "MATERIAL",
+ 450007
+ ],
+ "tier6_special": [
+ "tier6_special",
+ "tier6_special",
+ "特种皇家信物",
+ "MATERIAL",
+ 450008
+ ],
+ "特种皇家信物": [
+ "tier6_special",
+ "tier6_special",
+ "特种皇家信物",
+ "MATERIAL",
+ 450008
+ ],
+ "p_char_285_medic2": [
+ "p_char_285_medic2",
+ "p_char_285_medic2",
+ "Lancet-2的信物",
+ "MATERIAL",
+ 1100001
+ ],
+ "Lancet-2的信物": [
+ "p_char_285_medic2",
+ "p_char_285_medic2",
+ "Lancet-2的信物",
+ "MATERIAL",
+ 1100001
+ ],
+ "p_char_286_cast3": [
+ "p_char_286_cast3",
+ "p_char_286_cast3",
+ "Castle-3的信物",
+ "MATERIAL",
+ 1100002
+ ],
+ "Castle-3的信物": [
+ "p_char_286_cast3",
+ "p_char_286_cast3",
+ "Castle-3的信物",
+ "MATERIAL",
+ 1100002
+ ],
+ "p_char_376_therex": [
+ "p_char_376_therex",
+ "p_char_376_therex",
+ "THRM-EX的信物",
+ "MATERIAL",
+ 1100003
+ ],
+ "THRM-EX的信物": [
+ "p_char_376_therex",
+ "p_char_376_therex",
+ "THRM-EX的信物",
+ "MATERIAL",
+ 1100003
+ ],
+ "p_char_4000_jnight": [
+ "p_char_4000_jnight",
+ "p_char_4000_jnight",
+ "正义骑士号的信物",
+ "MATERIAL",
+ 1100004
+ ],
+ "正义骑士号的信物": [
+ "p_char_4000_jnight",
+ "p_char_4000_jnight",
+ "正义骑士号的信物",
+ "MATERIAL",
+ 1100004
+ ],
+ "p_char_4077_palico": [
+ "p_char_4077_palico",
+ "p_char_4077_palico",
+ "泰拉大陆调查团的信物",
+ "MATERIAL",
+ 1100005
+ ],
+ "泰拉大陆调查团的信物": [
+ "p_char_4077_palico",
+ "p_char_4077_palico",
+ "泰拉大陆调查团的信物",
+ "MATERIAL",
+ 1100005
+ ],
+ "p_char_4093_frston": [
+ "p_char_4093_frston",
+ "p_char_4093_frston",
+ "Friston-3的信物",
+ "MATERIAL",
+ 1100006
+ ],
+ "Friston-3的信物": [
+ "p_char_4093_frston",
+ "p_char_4093_frston",
+ "Friston-3的信物",
+ "MATERIAL",
+ 1100006
+ ],
+ "p_char_4136_phonor": [
+ "p_char_4136_phonor",
+ "p_char_4136_phonor",
+ "PhonoR-0的信物",
+ "MATERIAL",
+ 1100007
+ ],
+ "PhonoR-0的信物": [
+ "p_char_4136_phonor",
+ "p_char_4136_phonor",
+ "PhonoR-0的信物",
+ "MATERIAL",
+ 1100007
+ ],
+ "p_char_502_nblade": [
+ "p_char_502_nblade",
+ "p_char_502_nblade",
+ "夜刀的信物",
+ "MATERIAL",
+ 1000001
+ ],
+ "夜刀的信物": [
+ "p_char_502_nblade",
+ "p_char_502_nblade",
+ "夜刀的信物",
+ "MATERIAL",
+ 1000001
+ ],
+ "p_char_500_noirc": [
+ "p_char_500_noirc",
+ "p_char_500_noirc",
+ "黑角的信物",
+ "MATERIAL",
+ 1000002
+ ],
+ "黑角的信物": [
+ "p_char_500_noirc",
+ "p_char_500_noirc",
+ "黑角的信物",
+ "MATERIAL",
+ 1000002
+ ],
+ "p_char_503_rang": [
+ "p_char_503_rang",
+ "p_char_503_rang",
+ "巡林者的信物",
+ "MATERIAL",
+ 1000003
+ ],
+ "巡林者的信物": [
+ "p_char_503_rang",
+ "p_char_503_rang",
+ "巡林者的信物",
+ "MATERIAL",
+ 1000003
+ ],
+ "p_char_501_durin": [
+ "p_char_501_durin",
+ "p_char_501_durin",
+ "杜林的信物",
+ "MATERIAL",
+ 1000004
+ ],
+ "杜林的信物": [
+ "p_char_501_durin",
+ "p_char_501_durin",
+ "杜林的信物",
+ "MATERIAL",
+ 1000004
+ ],
+ "p_char_009_12fce": [
+ "p_char_009_12fce",
+ "p_char_009_12fce",
+ "12F的信物",
+ "MATERIAL",
+ 1000005
+ ],
+ "12F的信物": [
+ "p_char_009_12fce",
+ "p_char_009_12fce",
+ "12F的信物",
+ "MATERIAL",
+ 1000005
+ ],
+ "p_char_123_fang": [
+ "p_char_123_fang",
+ "p_char_123_fang",
+ "芬的信物",
+ "MATERIAL",
+ 900001
+ ],
+ "芬的信物": [
+ "p_char_123_fang",
+ "p_char_123_fang",
+ "芬的信物",
+ "MATERIAL",
+ 900001
+ ],
+ "p_char_240_wyvern": [
+ "p_char_240_wyvern",
+ "p_char_240_wyvern",
+ "香草的信物",
+ "MATERIAL",
+ 900002
+ ],
+ "香草的信物": [
+ "p_char_240_wyvern",
+ "p_char_240_wyvern",
+ "香草的信物",
+ "MATERIAL",
+ 900002
+ ],
+ "p_char_192_falco": [
+ "p_char_192_falco",
+ "p_char_192_falco",
+ "翎羽的信物",
+ "MATERIAL",
+ 900003
+ ],
+ "翎羽的信物": [
+ "p_char_192_falco",
+ "p_char_192_falco",
+ "翎羽的信物",
+ "MATERIAL",
+ 900003
+ ],
+ "p_char_208_melan": [
+ "p_char_208_melan",
+ "p_char_208_melan",
+ "玫兰莎的信物",
+ "MATERIAL",
+ 900004
+ ],
+ "玫兰莎的信物": [
+ "p_char_208_melan",
+ "p_char_208_melan",
+ "玫兰莎的信物",
+ "MATERIAL",
+ 900004
+ ],
+ "p_char_209_ardign": [
+ "p_char_209_ardign",
+ "p_char_209_ardign",
+ "卡缇的信物",
+ "MATERIAL",
+ 900005
+ ],
+ "卡缇的信物": [
+ "p_char_209_ardign",
+ "p_char_209_ardign",
+ "卡缇的信物",
+ "MATERIAL",
+ 900005
+ ],
+ "p_char_122_beagle": [
+ "p_char_122_beagle",
+ "p_char_122_beagle",
+ "米格鲁的信物",
+ "MATERIAL",
+ 900006
+ ],
+ "米格鲁的信物": [
+ "p_char_122_beagle",
+ "p_char_122_beagle",
+ "米格鲁的信物",
+ "MATERIAL",
+ 900006
+ ],
+ "p_char_124_kroos": [
+ "p_char_124_kroos",
+ "p_char_124_kroos",
+ "克洛丝的信物",
+ "MATERIAL",
+ 900007
+ ],
+ "克洛丝的信物": [
+ "p_char_124_kroos",
+ "p_char_124_kroos",
+ "克洛丝的信物",
+ "MATERIAL",
+ 900007
+ ],
+ "p_char_211_adnach": [
+ "p_char_211_adnach",
+ "p_char_211_adnach",
+ "安德切尔的信物",
+ "MATERIAL",
+ 900008
+ ],
+ "安德切尔的信物": [
+ "p_char_211_adnach",
+ "p_char_211_adnach",
+ "安德切尔的信物",
+ "MATERIAL",
+ 900008
+ ],
+ "p_char_121_lava": [
+ "p_char_121_lava",
+ "p_char_121_lava",
+ "炎熔的信物",
+ "MATERIAL",
+ 900009
+ ],
+ "炎熔的信物": [
+ "p_char_121_lava",
+ "p_char_121_lava",
+ "炎熔的信物",
+ "MATERIAL",
+ 900009
+ ],
+ "p_char_120_hibisc": [
+ "p_char_120_hibisc",
+ "p_char_120_hibisc",
+ "芙蓉的信物",
+ "MATERIAL",
+ 900010
+ ],
+ "芙蓉的信物": [
+ "p_char_120_hibisc",
+ "p_char_120_hibisc",
+ "芙蓉的信物",
+ "MATERIAL",
+ 900010
+ ],
+ "p_char_212_ansel": [
+ "p_char_212_ansel",
+ "p_char_212_ansel",
+ "安赛尔的信物",
+ "MATERIAL",
+ 900011
+ ],
+ "安赛尔的信物": [
+ "p_char_212_ansel",
+ "p_char_212_ansel",
+ "安赛尔的信物",
+ "MATERIAL",
+ 900011
+ ],
+ "p_char_210_stward": [
+ "p_char_210_stward",
+ "p_char_210_stward",
+ "史都华德的信物",
+ "MATERIAL",
+ 900012
+ ],
+ "史都华德的信物": [
+ "p_char_210_stward",
+ "p_char_210_stward",
+ "史都华德的信物",
+ "MATERIAL",
+ 900012
+ ],
+ "p_char_278_orchid": [
+ "p_char_278_orchid",
+ "p_char_278_orchid",
+ "梓兰的信物",
+ "MATERIAL",
+ 900013
+ ],
+ "梓兰的信物": [
+ "p_char_278_orchid",
+ "p_char_278_orchid",
+ "梓兰的信物",
+ "MATERIAL",
+ 900013
+ ],
+ "p_char_282_catap": [
+ "p_char_282_catap",
+ "p_char_282_catap",
+ "空爆的信物",
+ "MATERIAL",
+ 900014
+ ],
+ "空爆的信物": [
+ "p_char_282_catap",
+ "p_char_282_catap",
+ "空爆的信物",
+ "MATERIAL",
+ 900014
+ ],
+ "p_char_283_midn": [
+ "p_char_283_midn",
+ "p_char_283_midn",
+ "月见夜的信物",
+ "MATERIAL",
+ 900015
+ ],
+ "月见夜的信物": [
+ "p_char_283_midn",
+ "p_char_283_midn",
+ "月见夜的信物",
+ "MATERIAL",
+ 900015
+ ],
+ "p_char_284_spot": [
+ "p_char_284_spot",
+ "p_char_284_spot",
+ "斑点的信物",
+ "MATERIAL",
+ 900016
+ ],
+ "斑点的信物": [
+ "p_char_284_spot",
+ "p_char_284_spot",
+ "斑点的信物",
+ "MATERIAL",
+ 900016
+ ],
+ "p_char_281_popka": [
+ "p_char_281_popka",
+ "p_char_281_popka",
+ "泡普卡的信物",
+ "MATERIAL",
+ 900017
+ ],
+ "泡普卡的信物": [
+ "p_char_281_popka",
+ "p_char_281_popka",
+ "泡普卡的信物",
+ "MATERIAL",
+ 900017
+ ],
+ "p_char_141_nights": [
+ "p_char_141_nights",
+ "p_char_141_nights",
+ "夜烟的信物",
+ "MATERIAL",
+ 800001
+ ],
+ "夜烟的信物": [
+ "p_char_141_nights",
+ "p_char_141_nights",
+ "夜烟的信物",
+ "MATERIAL",
+ 800001
+ ],
+ "p_char_109_fmout": [
+ "p_char_109_fmout",
+ "p_char_109_fmout",
+ "远山的信物",
+ "MATERIAL",
+ 800002
+ ],
+ "远山的信物": [
+ "p_char_109_fmout",
+ "p_char_109_fmout",
+ "远山的信物",
+ "MATERIAL",
+ 800002
+ ],
+ "p_char_235_jesica": [
+ "p_char_235_jesica",
+ "p_char_235_jesica",
+ "杰西卡的信物",
+ "MATERIAL",
+ 800003
+ ],
+ "杰西卡的信物": [
+ "p_char_235_jesica",
+ "p_char_235_jesica",
+ "杰西卡的信物",
+ "MATERIAL",
+ 800003
+ ],
+ "p_char_126_shotst": [
+ "p_char_126_shotst",
+ "p_char_126_shotst",
+ "流星的信物",
+ "MATERIAL",
+ 800004
+ ],
+ "流星的信物": [
+ "p_char_126_shotst",
+ "p_char_126_shotst",
+ "流星的信物",
+ "MATERIAL",
+ 800004
+ ],
+ "p_char_118_yuki": [
+ "p_char_118_yuki",
+ "p_char_118_yuki",
+ "白雪的信物",
+ "MATERIAL",
+ 800005
+ ],
+ "白雪的信物": [
+ "p_char_118_yuki",
+ "p_char_118_yuki",
+ "白雪的信物",
+ "MATERIAL",
+ 800005
+ ],
+ "p_char_198_blackd": [
+ "p_char_198_blackd",
+ "p_char_198_blackd",
+ "讯使的信物",
+ "MATERIAL",
+ 800006
+ ],
+ "讯使的信物": [
+ "p_char_198_blackd",
+ "p_char_198_blackd",
+ "讯使的信物",
+ "MATERIAL",
+ 800006
+ ],
+ "p_char_149_scave": [
+ "p_char_149_scave",
+ "p_char_149_scave",
+ "清道夫的信物",
+ "MATERIAL",
+ 800007
+ ],
+ "清道夫的信物": [
+ "p_char_149_scave",
+ "p_char_149_scave",
+ "清道夫的信物",
+ "MATERIAL",
+ 800007
+ ],
+ "p_char_290_vigna": [
+ "p_char_290_vigna",
+ "p_char_290_vigna",
+ "红豆的信物",
+ "MATERIAL",
+ 800008
+ ],
+ "红豆的信物": [
+ "p_char_290_vigna",
+ "p_char_290_vigna",
+ "红豆的信物",
+ "MATERIAL",
+ 800008
+ ],
+ "p_char_130_doberm": [
+ "p_char_130_doberm",
+ "p_char_130_doberm",
+ "杜宾的信物",
+ "MATERIAL",
+ 800009
+ ],
+ "杜宾的信物": [
+ "p_char_130_doberm",
+ "p_char_130_doberm",
+ "杜宾的信物",
+ "MATERIAL",
+ 800009
+ ],
+ "p_char_289_gyuki": [
+ "p_char_289_gyuki",
+ "p_char_289_gyuki",
+ "缠丸的信物",
+ "MATERIAL",
+ 800010
+ ],
+ "缠丸的信物": [
+ "p_char_289_gyuki",
+ "p_char_289_gyuki",
+ "缠丸的信物",
+ "MATERIAL",
+ 800010
+ ],
+ "p_char_193_frostl": [
+ "p_char_193_frostl",
+ "p_char_193_frostl",
+ "霜叶的信物",
+ "MATERIAL",
+ 800011
+ ],
+ "霜叶的信物": [
+ "p_char_193_frostl",
+ "p_char_193_frostl",
+ "霜叶的信物",
+ "MATERIAL",
+ 800011
+ ],
+ "p_char_127_estell": [
+ "p_char_127_estell",
+ "p_char_127_estell",
+ "艾丝黛尔的信物",
+ "MATERIAL",
+ 800012
+ ],
+ "艾丝黛尔的信物": [
+ "p_char_127_estell",
+ "p_char_127_estell",
+ "艾丝黛尔的信物",
+ "MATERIAL",
+ 800012
+ ],
+ "p_char_185_frncat": [
+ "p_char_185_frncat",
+ "p_char_185_frncat",
+ "慕斯的信物",
+ "MATERIAL",
+ 800013
+ ],
+ "慕斯的信物": [
+ "p_char_185_frncat",
+ "p_char_185_frncat",
+ "慕斯的信物",
+ "MATERIAL",
+ 800013
+ ],
+ "p_char_237_gravel": [
+ "p_char_237_gravel",
+ "p_char_237_gravel",
+ "砾的信物",
+ "MATERIAL",
+ 800014
+ ],
+ "砾的信物": [
+ "p_char_237_gravel",
+ "p_char_237_gravel",
+ "砾的信物",
+ "MATERIAL",
+ 800014
+ ],
+ "p_char_236_rope": [
+ "p_char_236_rope",
+ "p_char_236_rope",
+ "暗索的信物",
+ "MATERIAL",
+ 800015
+ ],
+ "暗索的信物": [
+ "p_char_236_rope",
+ "p_char_236_rope",
+ "暗索的信物",
+ "MATERIAL",
+ 800015
+ ],
+ "p_char_117_myrrh": [
+ "p_char_117_myrrh",
+ "p_char_117_myrrh",
+ "末药的信物",
+ "MATERIAL",
+ 800016
+ ],
+ "末药的信物": [
+ "p_char_117_myrrh",
+ "p_char_117_myrrh",
+ "末药的信物",
+ "MATERIAL",
+ 800016
+ ],
+ "p_char_187_ccheal": [
+ "p_char_187_ccheal",
+ "p_char_187_ccheal",
+ "嘉维尔的信物",
+ "MATERIAL",
+ 800017
+ ],
+ "嘉维尔的信物": [
+ "p_char_187_ccheal",
+ "p_char_187_ccheal",
+ "嘉维尔的信物",
+ "MATERIAL",
+ 800017
+ ],
+ "p_char_181_flower": [
+ "p_char_181_flower",
+ "p_char_181_flower",
+ "调香师的信物",
+ "MATERIAL",
+ 800018
+ ],
+ "调香师的信物": [
+ "p_char_181_flower",
+ "p_char_181_flower",
+ "调香师的信物",
+ "MATERIAL",
+ 800018
+ ],
+ "p_char_199_yak": [
+ "p_char_199_yak",
+ "p_char_199_yak",
+ "角峰的信物",
+ "MATERIAL",
+ 800019
+ ],
+ "角峰的信物": [
+ "p_char_199_yak",
+ "p_char_199_yak",
+ "角峰的信物",
+ "MATERIAL",
+ 800019
+ ],
+ "p_char_150_snakek": [
+ "p_char_150_snakek",
+ "p_char_150_snakek",
+ "蛇屠箱的信物",
+ "MATERIAL",
+ 800020
+ ],
+ "蛇屠箱的信物": [
+ "p_char_150_snakek",
+ "p_char_150_snakek",
+ "蛇屠箱的信物",
+ "MATERIAL",
+ 800020
+ ],
+ "p_char_196_sunbr": [
+ "p_char_196_sunbr",
+ "p_char_196_sunbr",
+ "古米的信物",
+ "MATERIAL",
+ 800021
+ ],
+ "古米的信物": [
+ "p_char_196_sunbr",
+ "p_char_196_sunbr",
+ "古米的信物",
+ "MATERIAL",
+ 800021
+ ],
+ "p_char_110_deepcl": [
+ "p_char_110_deepcl",
+ "p_char_110_deepcl",
+ "深海色的信物",
+ "MATERIAL",
+ 800022
+ ],
+ "深海色的信物": [
+ "p_char_110_deepcl",
+ "p_char_110_deepcl",
+ "深海色的信物",
+ "MATERIAL",
+ 800022
+ ],
+ "p_char_183_skgoat": [
+ "p_char_183_skgoat",
+ "p_char_183_skgoat",
+ "地灵的信物",
+ "MATERIAL",
+ 800023
+ ],
+ "地灵的信物": [
+ "p_char_183_skgoat",
+ "p_char_183_skgoat",
+ "地灵的信物",
+ "MATERIAL",
+ 800023
+ ],
+ "p_char_277_sqrrel": [
+ "p_char_277_sqrrel",
+ "p_char_277_sqrrel",
+ "阿消的信物",
+ "MATERIAL",
+ 800024
+ ],
+ "阿消的信物": [
+ "p_char_277_sqrrel",
+ "p_char_277_sqrrel",
+ "阿消的信物",
+ "MATERIAL",
+ 800024
+ ],
+ "p_char_137_brownb": [
+ "p_char_137_brownb",
+ "p_char_137_brownb",
+ "猎蜂的信物",
+ "MATERIAL",
+ 800025
+ ],
+ "猎蜂的信物": [
+ "p_char_137_brownb",
+ "p_char_137_brownb",
+ "猎蜂的信物",
+ "MATERIAL",
+ 800025
+ ],
+ "p_char_253_greyy": [
+ "p_char_253_greyy",
+ "p_char_253_greyy",
+ "格雷伊的信物",
+ "MATERIAL",
+ 800026
+ ],
+ "格雷伊的信物": [
+ "p_char_253_greyy",
+ "p_char_253_greyy",
+ "格雷伊的信物",
+ "MATERIAL",
+ 800026
+ ],
+ "p_char_151_myrtle": [
+ "p_char_151_myrtle",
+ "p_char_151_myrtle",
+ "桃金娘的信物",
+ "MATERIAL",
+ 800027
+ ],
+ "桃金娘的信物": [
+ "p_char_151_myrtle",
+ "p_char_151_myrtle",
+ "桃金娘的信物",
+ "MATERIAL",
+ 800027
+ ],
+ "p_char_298_susuro": [
+ "p_char_298_susuro",
+ "p_char_298_susuro",
+ "苏苏洛的信物",
+ "MATERIAL",
+ 800028
+ ],
+ "苏苏洛的信物": [
+ "p_char_298_susuro",
+ "p_char_298_susuro",
+ "苏苏洛的信物",
+ "MATERIAL",
+ 800028
+ ],
+ "p_char_260_durnar": [
+ "p_char_260_durnar",
+ "p_char_260_durnar",
+ "坚雷的信物",
+ "MATERIAL",
+ 800029
+ ],
+ "坚雷的信物": [
+ "p_char_260_durnar",
+ "p_char_260_durnar",
+ "坚雷的信物",
+ "MATERIAL",
+ 800029
+ ],
+ "p_char_355_ethan": [
+ "p_char_355_ethan",
+ "p_char_355_ethan",
+ "伊桑的信物",
+ "MATERIAL",
+ 800030
+ ],
+ "伊桑的信物": [
+ "p_char_355_ethan",
+ "p_char_355_ethan",
+ "伊桑的信物",
+ "MATERIAL",
+ 800030
+ ],
+ "p_char_190_clour": [
+ "p_char_190_clour",
+ "p_char_190_clour",
+ "红云的信物",
+ "MATERIAL",
+ 800031
+ ],
+ "红云的信物": [
+ "p_char_190_clour",
+ "p_char_190_clour",
+ "红云的信物",
+ "MATERIAL",
+ 800031
+ ],
+ "p_char_133_mm": [
+ "p_char_133_mm",
+ "p_char_133_mm",
+ "梅的信物",
+ "MATERIAL",
+ 800032
+ ],
+ "梅的信物": [
+ "p_char_133_mm",
+ "p_char_133_mm",
+ "梅的信物",
+ "MATERIAL",
+ 800032
+ ],
+ "p_char_302_glaze": [
+ "p_char_302_glaze",
+ "p_char_302_glaze",
+ "安比尔的信物",
+ "MATERIAL",
+ 800033
+ ],
+ "安比尔的信物": [
+ "p_char_302_glaze",
+ "p_char_302_glaze",
+ "安比尔的信物",
+ "MATERIAL",
+ 800033
+ ],
+ "p_char_385_finlpp": [
+ "p_char_385_finlpp",
+ "p_char_385_finlpp",
+ "清流的信物",
+ "MATERIAL",
+ 800034
+ ],
+ "清流的信物": [
+ "p_char_385_finlpp",
+ "p_char_385_finlpp",
+ "清流的信物",
+ "MATERIAL",
+ 800034
+ ],
+ "p_char_337_utage": [
+ "p_char_337_utage",
+ "p_char_337_utage",
+ "宴的信物",
+ "MATERIAL",
+ 800035
+ ],
+ "宴的信物": [
+ "p_char_337_utage",
+ "p_char_337_utage",
+ "宴的信物",
+ "MATERIAL",
+ 800035
+ ],
+ "p_char_301_cutter": [
+ "p_char_301_cutter",
+ "p_char_301_cutter",
+ "刻刀的信物",
+ "MATERIAL",
+ 800036
+ ],
+ "刻刀的信物": [
+ "p_char_301_cutter",
+ "p_char_301_cutter",
+ "刻刀的信物",
+ "MATERIAL",
+ 800036
+ ],
+ "p_char_258_podego": [
+ "p_char_258_podego",
+ "p_char_258_podego",
+ "波登可的信物",
+ "MATERIAL",
+ 800037
+ ],
+ "波登可的信物": [
+ "p_char_258_podego",
+ "p_char_258_podego",
+ "波登可的信物",
+ "MATERIAL",
+ 800037
+ ],
+ "p_char_328_cammou": [
+ "p_char_328_cammou",
+ "p_char_328_cammou",
+ "卡达的信物",
+ "MATERIAL",
+ 800038
+ ],
+ "卡达的信物": [
+ "p_char_328_cammou",
+ "p_char_328_cammou",
+ "卡达的信物",
+ "MATERIAL",
+ 800038
+ ],
+ "p_char_272_strong": [
+ "p_char_272_strong",
+ "p_char_272_strong",
+ "孑的信物",
+ "MATERIAL",
+ 800039
+ ],
+ "孑的信物": [
+ "p_char_272_strong",
+ "p_char_272_strong",
+ "孑的信物",
+ "MATERIAL",
+ 800039
+ ],
+ "p_char_366_acdrop": [
+ "p_char_366_acdrop",
+ "p_char_366_acdrop",
+ "酸糖的信物",
+ "MATERIAL",
+ 800040
+ ],
+ "酸糖的信物": [
+ "p_char_366_acdrop",
+ "p_char_366_acdrop",
+ "酸糖的信物",
+ "MATERIAL",
+ 800040
+ ],
+ "p_char_271_spikes": [
+ "p_char_271_spikes",
+ "p_char_271_spikes",
+ "芳汀的信物",
+ "MATERIAL",
+ 800041
+ ],
+ "芳汀的信物": [
+ "p_char_271_spikes",
+ "p_char_271_spikes",
+ "芳汀的信物",
+ "MATERIAL",
+ 800041
+ ],
+ "p_char_381_bubble": [
+ "p_char_381_bubble",
+ "p_char_381_bubble",
+ "泡泡的信物",
+ "MATERIAL",
+ 800042
+ ],
+ "泡泡的信物": [
+ "p_char_381_bubble",
+ "p_char_381_bubble",
+ "泡泡的信物",
+ "MATERIAL",
+ 800042
+ ],
+ "p_char_347_jaksel": [
+ "p_char_347_jaksel",
+ "p_char_347_jaksel",
+ "杰克的信物",
+ "MATERIAL",
+ 800043
+ ],
+ "杰克的信物": [
+ "p_char_347_jaksel",
+ "p_char_347_jaksel",
+ "杰克的信物",
+ "MATERIAL",
+ 800043
+ ],
+ "p_char_440_pinecn": [
+ "p_char_440_pinecn",
+ "p_char_440_pinecn",
+ "松果的信物",
+ "MATERIAL",
+ 800044
+ ],
+ "松果的信物": [
+ "p_char_440_pinecn",
+ "p_char_440_pinecn",
+ "松果的信物",
+ "MATERIAL",
+ 800044
+ ],
+ "p_char_452_bstalk": [
+ "p_char_452_bstalk",
+ "p_char_452_bstalk",
+ "豆苗的信物",
+ "MATERIAL",
+ 800045
+ ],
+ "豆苗的信物": [
+ "p_char_452_bstalk",
+ "p_char_452_bstalk",
+ "豆苗的信物",
+ "MATERIAL",
+ 800045
+ ],
+ "p_char_469_indigo": [
+ "p_char_469_indigo",
+ "p_char_469_indigo",
+ "深靛的信物",
+ "MATERIAL",
+ 800046
+ ],
+ "深靛的信物": [
+ "p_char_469_indigo",
+ "p_char_469_indigo",
+ "深靛的信物",
+ "MATERIAL",
+ 800046
+ ],
+ "p_char_484_robrta": [
+ "p_char_484_robrta",
+ "p_char_484_robrta",
+ "罗比菈塔的信物",
+ "MATERIAL",
+ 800047
+ ],
+ "罗比菈塔的信物": [
+ "p_char_484_robrta",
+ "p_char_484_robrta",
+ "罗比菈塔的信物",
+ "MATERIAL",
+ 800047
+ ],
+ "p_char_4004_pudd": [
+ "p_char_4004_pudd",
+ "p_char_4004_pudd",
+ "布丁的信物",
+ "MATERIAL",
+ 800048
+ ],
+ "布丁的信物": [
+ "p_char_4004_pudd",
+ "p_char_4004_pudd",
+ "布丁的信物",
+ "MATERIAL",
+ 800048
+ ],
+ "p_char_4041_chnut": [
+ "p_char_4041_chnut",
+ "p_char_4041_chnut",
+ "褐果的信物",
+ "MATERIAL",
+ 800049
+ ],
+ "褐果的信物": [
+ "p_char_4041_chnut",
+ "p_char_4041_chnut",
+ "褐果的信物",
+ "MATERIAL",
+ 800049
+ ],
+ "p_char_4062_totter": [
+ "p_char_4062_totter",
+ "p_char_4062_totter",
+ "铅踝的信物",
+ "MATERIAL",
+ 800050
+ ],
+ "铅踝的信物": [
+ "p_char_4062_totter",
+ "p_char_4062_totter",
+ "铅踝的信物",
+ "MATERIAL",
+ 800050
+ ],
+ "p_char_4063_quartz": [
+ "p_char_4063_quartz",
+ "p_char_4063_quartz",
+ "石英的信物",
+ "MATERIAL",
+ 800051
+ ],
+ "石英的信物": [
+ "p_char_4063_quartz",
+ "p_char_4063_quartz",
+ "石英的信物",
+ "MATERIAL",
+ 800051
+ ],
+ "p_char_491_humus": [
+ "p_char_491_humus",
+ "p_char_491_humus",
+ "休谟斯的信物",
+ "MATERIAL",
+ 800052
+ ],
+ "休谟斯的信物": [
+ "p_char_491_humus",
+ "p_char_491_humus",
+ "休谟斯的信物",
+ "MATERIAL",
+ 800052
+ ],
+ "p_char_4107_vrdant": [
+ "p_char_4107_vrdant",
+ "p_char_4107_vrdant",
+ "维荻的信物",
+ "MATERIAL",
+ 800053
+ ],
+ "维荻的信物": [
+ "p_char_4107_vrdant",
+ "p_char_4107_vrdant",
+ "维荻的信物",
+ "MATERIAL",
+ 800053
+ ],
+ "p_char_4100_caper": [
+ "p_char_4100_caper",
+ "p_char_4100_caper",
+ "跃跃的信物",
+ "MATERIAL",
+ 800054
+ ],
+ "跃跃的信物": [
+ "p_char_4100_caper",
+ "p_char_4100_caper",
+ "跃跃的信物",
+ "MATERIAL",
+ 800054
+ ],
+ "p_char_4130_luton": [
+ "p_char_4130_luton",
+ "p_char_4130_luton",
+ "露托的信物",
+ "MATERIAL",
+ 800055
+ ],
+ "露托的信物": [
+ "p_char_4130_luton",
+ "p_char_4130_luton",
+ "露托的信物",
+ "MATERIAL",
+ 800055
+ ],
+ "p_char_128_plosis": [
+ "p_char_128_plosis",
+ "p_char_128_plosis",
+ "白面鸮的信物",
+ "MATERIAL",
+ 700001
+ ],
+ "白面鸮的信物": [
+ "p_char_128_plosis",
+ "p_char_128_plosis",
+ "白面鸮的信物",
+ "MATERIAL",
+ 700001
+ ],
+ "p_char_115_headbr": [
+ "p_char_115_headbr",
+ "p_char_115_headbr",
+ "凛冬的信物",
+ "MATERIAL",
+ 700002
+ ],
+ "凛冬的信物": [
+ "p_char_115_headbr",
+ "p_char_115_headbr",
+ "凛冬的信物",
+ "MATERIAL",
+ 700002
+ ],
+ "p_char_102_texas": [
+ "p_char_102_texas",
+ "p_char_102_texas",
+ "德克萨斯的信物",
+ "MATERIAL",
+ 700003
+ ],
+ "德克萨斯的信物": [
+ "p_char_102_texas",
+ "p_char_102_texas",
+ "德克萨斯的信物",
+ "MATERIAL",
+ 700003
+ ],
+ "p_char_106_franka": [
+ "p_char_106_franka",
+ "p_char_106_franka",
+ "芙兰卡的信物",
+ "MATERIAL",
+ 700004
+ ],
+ "芙兰卡的信物": [
+ "p_char_106_franka",
+ "p_char_106_franka",
+ "芙兰卡的信物",
+ "MATERIAL",
+ 700004
+ ],
+ "p_char_155_tiger": [
+ "p_char_155_tiger",
+ "p_char_155_tiger",
+ "因陀罗的信物",
+ "MATERIAL",
+ 700005
+ ],
+ "因陀罗的信物": [
+ "p_char_155_tiger",
+ "p_char_155_tiger",
+ "因陀罗的信物",
+ "MATERIAL",
+ 700005
+ ],
+ "p_char_140_whitew": [
+ "p_char_140_whitew",
+ "p_char_140_whitew",
+ "拉普兰德的信物",
+ "MATERIAL",
+ 700006
+ ],
+ "拉普兰德的信物": [
+ "p_char_140_whitew",
+ "p_char_140_whitew",
+ "拉普兰德的信物",
+ "MATERIAL",
+ 700006
+ ],
+ "p_char_143_ghost": [
+ "p_char_143_ghost",
+ "p_char_143_ghost",
+ "幽灵鲨的信物",
+ "MATERIAL",
+ 700007
+ ],
+ "幽灵鲨的信物": [
+ "p_char_143_ghost",
+ "p_char_143_ghost",
+ "幽灵鲨的信物",
+ "MATERIAL",
+ 700007
+ ],
+ "p_char_129_bluep": [
+ "p_char_129_bluep",
+ "p_char_129_bluep",
+ "蓝毒的信物",
+ "MATERIAL",
+ 700008
+ ],
+ "蓝毒的信物": [
+ "p_char_129_bluep",
+ "p_char_129_bluep",
+ "蓝毒的信物",
+ "MATERIAL",
+ 700008
+ ],
+ "p_char_204_platnm": [
+ "p_char_204_platnm",
+ "p_char_204_platnm",
+ "白金的信物",
+ "MATERIAL",
+ 700009
+ ],
+ "白金的信物": [
+ "p_char_204_platnm",
+ "p_char_204_platnm",
+ "白金的信物",
+ "MATERIAL",
+ 700009
+ ],
+ "p_char_219_meteo": [
+ "p_char_219_meteo",
+ "p_char_219_meteo",
+ "陨星的信物",
+ "MATERIAL",
+ 700010
+ ],
+ "陨星的信物": [
+ "p_char_219_meteo",
+ "p_char_219_meteo",
+ "陨星的信物",
+ "MATERIAL",
+ 700010
+ ],
+ "p_char_002_amiya": [
+ "p_char_002_amiya",
+ "p_char_002_amiya",
+ "阿米娅的信物",
+ "MATERIAL",
+ 700011
+ ],
+ "阿米娅的信物": [
+ "p_char_002_amiya",
+ "p_char_002_amiya",
+ "阿米娅的信物",
+ "MATERIAL",
+ 700011
+ ],
+ "p_char_166_skfire": [
+ "p_char_166_skfire",
+ "p_char_166_skfire",
+ "天火的信物",
+ "MATERIAL",
+ 700012
+ ],
+ "天火的信物": [
+ "p_char_166_skfire",
+ "p_char_166_skfire",
+ "天火的信物",
+ "MATERIAL",
+ 700012
+ ],
+ "p_char_242_otter": [
+ "p_char_242_otter",
+ "p_char_242_otter",
+ "梅尔的信物",
+ "MATERIAL",
+ 700013
+ ],
+ "梅尔的信物": [
+ "p_char_242_otter",
+ "p_char_242_otter",
+ "梅尔的信物",
+ "MATERIAL",
+ 700013
+ ],
+ "p_char_108_silent": [
+ "p_char_108_silent",
+ "p_char_108_silent",
+ "赫默的信物",
+ "MATERIAL",
+ 700014
+ ],
+ "赫默的信物": [
+ "p_char_108_silent",
+ "p_char_108_silent",
+ "赫默的信物",
+ "MATERIAL",
+ 700014
+ ],
+ "p_char_171_bldsk": [
+ "p_char_171_bldsk",
+ "p_char_171_bldsk",
+ "华法琳的信物",
+ "MATERIAL",
+ 700015
+ ],
+ "华法琳的信物": [
+ "p_char_171_bldsk",
+ "p_char_171_bldsk",
+ "华法琳的信物",
+ "MATERIAL",
+ 700015
+ ],
+ "p_char_148_nearl": [
+ "p_char_148_nearl",
+ "p_char_148_nearl",
+ "临光的信物",
+ "MATERIAL",
+ 700016
+ ],
+ "临光的信物": [
+ "p_char_148_nearl",
+ "p_char_148_nearl",
+ "临光的信物",
+ "MATERIAL",
+ 700016
+ ],
+ "p_char_144_red": [
+ "p_char_144_red",
+ "p_char_144_red",
+ "红的信物",
+ "MATERIAL",
+ 700017
+ ],
+ "红的信物": [
+ "p_char_144_red",
+ "p_char_144_red",
+ "红的信物",
+ "MATERIAL",
+ 700017
+ ],
+ "p_char_107_liskam": [
+ "p_char_107_liskam",
+ "p_char_107_liskam",
+ "雷蛇的信物",
+ "MATERIAL",
+ 700018
+ ],
+ "雷蛇的信物": [
+ "p_char_107_liskam",
+ "p_char_107_liskam",
+ "雷蛇的信物",
+ "MATERIAL",
+ 700018
+ ],
+ "p_char_201_moeshd": [
+ "p_char_201_moeshd",
+ "p_char_201_moeshd",
+ "可颂的信物",
+ "MATERIAL",
+ 700019
+ ],
+ "可颂的信物": [
+ "p_char_201_moeshd",
+ "p_char_201_moeshd",
+ "可颂的信物",
+ "MATERIAL",
+ 700019
+ ],
+ "p_char_163_hpsts": [
+ "p_char_163_hpsts",
+ "p_char_163_hpsts",
+ "火神的信物",
+ "MATERIAL",
+ 700020
+ ],
+ "火神的信物": [
+ "p_char_163_hpsts",
+ "p_char_163_hpsts",
+ "火神的信物",
+ "MATERIAL",
+ 700020
+ ],
+ "p_char_145_prove": [
+ "p_char_145_prove",
+ "p_char_145_prove",
+ "普罗旺斯的信物",
+ "MATERIAL",
+ 700021
+ ],
+ "普罗旺斯的信物": [
+ "p_char_145_prove",
+ "p_char_145_prove",
+ "普罗旺斯的信物",
+ "MATERIAL",
+ 700021
+ ],
+ "p_char_158_milu": [
+ "p_char_158_milu",
+ "p_char_158_milu",
+ "守林人的信物",
+ "MATERIAL",
+ 700022
+ ],
+ "守林人的信物": [
+ "p_char_158_milu",
+ "p_char_158_milu",
+ "守林人的信物",
+ "MATERIAL",
+ 700022
+ ],
+ "p_char_173_slchan": [
+ "p_char_173_slchan",
+ "p_char_173_slchan",
+ "崖心的信物",
+ "MATERIAL",
+ 700023
+ ],
+ "崖心的信物": [
+ "p_char_173_slchan",
+ "p_char_173_slchan",
+ "崖心的信物",
+ "MATERIAL",
+ 700023
+ ],
+ "p_char_174_slbell": [
+ "p_char_174_slbell",
+ "p_char_174_slbell",
+ "初雪的信物",
+ "MATERIAL",
+ 700024
+ ],
+ "初雪的信物": [
+ "p_char_174_slbell",
+ "p_char_174_slbell",
+ "初雪的信物",
+ "MATERIAL",
+ 700024
+ ],
+ "p_char_195_glassb": [
+ "p_char_195_glassb",
+ "p_char_195_glassb",
+ "真理的信物",
+ "MATERIAL",
+ 700025
+ ],
+ "真理的信物": [
+ "p_char_195_glassb",
+ "p_char_195_glassb",
+ "真理的信物",
+ "MATERIAL",
+ 700025
+ ],
+ "p_char_101_sora": [
+ "p_char_101_sora",
+ "p_char_101_sora",
+ "空的信物",
+ "MATERIAL",
+ 700026
+ ],
+ "空的信物": [
+ "p_char_101_sora",
+ "p_char_101_sora",
+ "空的信物",
+ "MATERIAL",
+ 700026
+ ],
+ "p_char_215_mantic": [
+ "p_char_215_mantic",
+ "p_char_215_mantic",
+ "狮蝎的信物",
+ "MATERIAL",
+ 700027
+ ],
+ "狮蝎的信物": [
+ "p_char_215_mantic",
+ "p_char_215_mantic",
+ "狮蝎的信物",
+ "MATERIAL",
+ 700027
+ ],
+ "p_char_241_panda": [
+ "p_char_241_panda",
+ "p_char_241_panda",
+ "食铁兽的信物",
+ "MATERIAL",
+ 700028
+ ],
+ "食铁兽的信物": [
+ "p_char_241_panda",
+ "p_char_241_panda",
+ "食铁兽的信物",
+ "MATERIAL",
+ 700028
+ ],
+ "p_char_220_grani": [
+ "p_char_220_grani",
+ "p_char_220_grani",
+ "格拉尼的信物",
+ "MATERIAL",
+ 700029
+ ],
+ "格拉尼的信物": [
+ "p_char_220_grani",
+ "p_char_220_grani",
+ "格拉尼的信物",
+ "MATERIAL",
+ 700029
+ ],
+ "p_char_164_nightm": [
+ "p_char_164_nightm",
+ "p_char_164_nightm",
+ "夜魔的信物",
+ "MATERIAL",
+ 700030
+ ],
+ "夜魔的信物": [
+ "p_char_164_nightm",
+ "p_char_164_nightm",
+ "夜魔的信物",
+ "MATERIAL",
+ 700030
+ ],
+ "p_char_308_swire": [
+ "p_char_308_swire",
+ "p_char_308_swire",
+ "诗怀雅的信物",
+ "MATERIAL",
+ 700031
+ ],
+ "诗怀雅的信物": [
+ "p_char_308_swire",
+ "p_char_308_swire",
+ "诗怀雅的信物",
+ "MATERIAL",
+ 700031
+ ],
+ "p_char_274_astesi": [
+ "p_char_274_astesi",
+ "p_char_274_astesi",
+ "星极的信物",
+ "MATERIAL",
+ 700032
+ ],
+ "星极的信物": [
+ "p_char_274_astesi",
+ "p_char_274_astesi",
+ "星极的信物",
+ "MATERIAL",
+ 700032
+ ],
+ "p_char_348_ceylon": [
+ "p_char_348_ceylon",
+ "p_char_348_ceylon",
+ "锡兰的信物",
+ "MATERIAL",
+ 700033
+ ],
+ "锡兰的信物": [
+ "p_char_348_ceylon",
+ "p_char_348_ceylon",
+ "锡兰的信物",
+ "MATERIAL",
+ 700033
+ ],
+ "p_char_326_glacus": [
+ "p_char_326_glacus",
+ "p_char_326_glacus",
+ "格劳克斯的信物",
+ "MATERIAL",
+ 700034
+ ],
+ "格劳克斯的信物": [
+ "p_char_326_glacus",
+ "p_char_326_glacus",
+ "格劳克斯的信物",
+ "MATERIAL",
+ 700034
+ ],
+ "p_char_275_breeze": [
+ "p_char_275_breeze",
+ "p_char_275_breeze",
+ "微风的信物",
+ "MATERIAL",
+ 700035
+ ],
+ "微风的信物": [
+ "p_char_275_breeze",
+ "p_char_275_breeze",
+ "微风的信物",
+ "MATERIAL",
+ 700035
+ ],
+ "p_char_131_flameb": [
+ "p_char_131_flameb",
+ "p_char_131_flameb",
+ "炎客的信物",
+ "MATERIAL",
+ 700036
+ ],
+ "炎客的信物": [
+ "p_char_131_flameb",
+ "p_char_131_flameb",
+ "炎客的信物",
+ "MATERIAL",
+ 700036
+ ],
+ "p_char_279_excu": [
+ "p_char_279_excu",
+ "p_char_279_excu",
+ "送葬人的信物",
+ "MATERIAL",
+ 700037
+ ],
+ "送葬人的信物": [
+ "p_char_279_excu",
+ "p_char_279_excu",
+ "送葬人的信物",
+ "MATERIAL",
+ 700037
+ ],
+ "p_char_261_sddrag": [
+ "p_char_261_sddrag",
+ "p_char_261_sddrag",
+ "苇草的信物",
+ "MATERIAL",
+ 700038
+ ],
+ "苇草的信物": [
+ "p_char_261_sddrag",
+ "p_char_261_sddrag",
+ "苇草的信物",
+ "MATERIAL",
+ 700038
+ ],
+ "p_char_356_broca": [
+ "p_char_356_broca",
+ "p_char_356_broca",
+ "布洛卡的信物",
+ "MATERIAL",
+ 700039
+ ],
+ "布洛卡的信物": [
+ "p_char_356_broca",
+ "p_char_356_broca",
+ "布洛卡的信物",
+ "MATERIAL",
+ 700039
+ ],
+ "p_char_243_waaifu": [
+ "p_char_243_waaifu",
+ "p_char_243_waaifu",
+ "槐琥的信物",
+ "MATERIAL",
+ 700040
+ ],
+ "槐琥的信物": [
+ "p_char_243_waaifu",
+ "p_char_243_waaifu",
+ "槐琥的信物",
+ "MATERIAL",
+ 700040
+ ],
+ "p_char_325_bison": [
+ "p_char_325_bison",
+ "p_char_325_bison",
+ "拜松的信物",
+ "MATERIAL",
+ 700041
+ ],
+ "拜松的信物": [
+ "p_char_325_bison",
+ "p_char_325_bison",
+ "拜松的信物",
+ "MATERIAL",
+ 700041
+ ],
+ "p_char_367_swllow": [
+ "p_char_367_swllow",
+ "p_char_367_swllow",
+ "灰喉的信物",
+ "MATERIAL",
+ 700042
+ ],
+ "灰喉的信物": [
+ "p_char_367_swllow",
+ "p_char_367_swllow",
+ "灰喉的信物",
+ "MATERIAL",
+ 700042
+ ],
+ "p_char_226_hmau": [
+ "p_char_226_hmau",
+ "p_char_226_hmau",
+ "吽的信物",
+ "MATERIAL",
+ 700043
+ ],
+ "吽的信物": [
+ "p_char_226_hmau",
+ "p_char_226_hmau",
+ "吽的信物",
+ "MATERIAL",
+ 700043
+ ],
+ "p_char_383_snsant": [
+ "p_char_383_snsant",
+ "p_char_383_snsant",
+ "雪雉的信物",
+ "MATERIAL",
+ 700044
+ ],
+ "雪雉的信物": [
+ "p_char_383_snsant",
+ "p_char_383_snsant",
+ "雪雉的信物",
+ "MATERIAL",
+ 700044
+ ],
+ "p_char_306_leizi": [
+ "p_char_306_leizi",
+ "p_char_306_leizi",
+ "惊蛰的信物",
+ "MATERIAL",
+ 700045
+ ],
+ "惊蛰的信物": [
+ "p_char_306_leizi",
+ "p_char_306_leizi",
+ "惊蛰的信物",
+ "MATERIAL",
+ 700045
+ ],
+ "p_char_379_sesa": [
+ "p_char_379_sesa",
+ "p_char_379_sesa",
+ "慑砂的信物",
+ "MATERIAL",
+ 700046
+ ],
+ "慑砂的信物": [
+ "p_char_379_sesa",
+ "p_char_379_sesa",
+ "慑砂的信物",
+ "MATERIAL",
+ 700046
+ ],
+ "p_char_252_bibeak": [
+ "p_char_252_bibeak",
+ "p_char_252_bibeak",
+ "柏喙的信物",
+ "MATERIAL",
+ 700047
+ ],
+ "柏喙的信物": [
+ "p_char_252_bibeak",
+ "p_char_252_bibeak",
+ "柏喙的信物",
+ "MATERIAL",
+ 700047
+ ],
+ "p_char_230_savage": [
+ "p_char_230_savage",
+ "p_char_230_savage",
+ "暴行的信物",
+ "MATERIAL",
+ 700048
+ ],
+ "暴行的信物": [
+ "p_char_230_savage",
+ "p_char_230_savage",
+ "暴行的信物",
+ "MATERIAL",
+ 700048
+ ],
+ "p_char_254_vodfox": [
+ "p_char_254_vodfox",
+ "p_char_254_vodfox",
+ "巫恋的信物",
+ "MATERIAL",
+ 700048
+ ],
+ "巫恋的信物": [
+ "p_char_254_vodfox",
+ "p_char_254_vodfox",
+ "巫恋的信物",
+ "MATERIAL",
+ 700048
+ ],
+ "p_char_333_sidero": [
+ "p_char_333_sidero",
+ "p_char_333_sidero",
+ "铸铁的信物",
+ "MATERIAL",
+ 700049
+ ],
+ "铸铁的信物": [
+ "p_char_333_sidero",
+ "p_char_333_sidero",
+ "铸铁的信物",
+ "MATERIAL",
+ 700049
+ ],
+ "p_char_401_elysm": [
+ "p_char_401_elysm",
+ "p_char_401_elysm",
+ "极境的信物",
+ "MATERIAL",
+ 700050
+ ],
+ "极境的信物": [
+ "p_char_401_elysm",
+ "p_char_401_elysm",
+ "极境的信物",
+ "MATERIAL",
+ 700050
+ ],
+ "p_char_378_asbest": [
+ "p_char_378_asbest",
+ "p_char_378_asbest",
+ "石棉的信物",
+ "MATERIAL",
+ 700051
+ ],
+ "石棉的信物": [
+ "p_char_378_asbest",
+ "p_char_378_asbest",
+ "石棉的信物",
+ "MATERIAL",
+ 700051
+ ],
+ "p_char_343_tknogi": [
+ "p_char_343_tknogi",
+ "p_char_343_tknogi",
+ "月禾的信物",
+ "MATERIAL",
+ 700052
+ ],
+ "月禾的信物": [
+ "p_char_343_tknogi",
+ "p_char_343_tknogi",
+ "月禾的信物",
+ "MATERIAL",
+ 700052
+ ],
+ "p_char_405_absin": [
+ "p_char_405_absin",
+ "p_char_405_absin",
+ "苦艾的信物",
+ "MATERIAL",
+ 700053
+ ],
+ "苦艾的信物": [
+ "p_char_405_absin",
+ "p_char_405_absin",
+ "苦艾的信物",
+ "MATERIAL",
+ 700053
+ ],
+ "p_char_373_lionhd": [
+ "p_char_373_lionhd",
+ "p_char_373_lionhd",
+ "莱恩哈特的信物",
+ "MATERIAL",
+ 700054
+ ],
+ "莱恩哈特的信物": [
+ "p_char_373_lionhd",
+ "p_char_373_lionhd",
+ "莱恩哈特的信物",
+ "MATERIAL",
+ 700054
+ ],
+ "p_char_294_ayer": [
+ "p_char_294_ayer",
+ "p_char_294_ayer",
+ "断崖的信物",
+ "MATERIAL",
+ 700055
+ ],
+ "断崖的信物": [
+ "p_char_294_ayer",
+ "p_char_294_ayer",
+ "断崖的信物",
+ "MATERIAL",
+ 700055
+ ],
+ "p_char_345_folnic": [
+ "p_char_345_folnic",
+ "p_char_345_folnic",
+ "亚叶的信物",
+ "MATERIAL",
+ 700056
+ ],
+ "亚叶的信物": [
+ "p_char_345_folnic",
+ "p_char_345_folnic",
+ "亚叶的信物",
+ "MATERIAL",
+ 700056
+ ],
+ "p_char_344_beewax": [
+ "p_char_344_beewax",
+ "p_char_344_beewax",
+ "蜜蜡的信物",
+ "MATERIAL",
+ 700057
+ ],
+ "蜜蜡的信物": [
+ "p_char_344_beewax",
+ "p_char_344_beewax",
+ "蜜蜡的信物",
+ "MATERIAL",
+ 700057
+ ],
+ "p_char_349_chiave": [
+ "p_char_349_chiave",
+ "p_char_349_chiave",
+ "贾维的信物",
+ "MATERIAL",
+ 700058
+ ],
+ "贾维的信物": [
+ "p_char_349_chiave",
+ "p_char_349_chiave",
+ "贾维的信物",
+ "MATERIAL",
+ 700058
+ ],
+ "p_char_336_folivo": [
+ "p_char_336_folivo",
+ "p_char_336_folivo",
+ "稀音的信物",
+ "MATERIAL",
+ 700059
+ ],
+ "稀音的信物": [
+ "p_char_336_folivo",
+ "p_char_336_folivo",
+ "稀音的信物",
+ "MATERIAL",
+ 700059
+ ],
+ "p_char_218_cuttle": [
+ "p_char_218_cuttle",
+ "p_char_218_cuttle",
+ "安哲拉的信物",
+ "MATERIAL",
+ 700060
+ ],
+ "安哲拉的信物": [
+ "p_char_218_cuttle",
+ "p_char_218_cuttle",
+ "安哲拉的信物",
+ "MATERIAL",
+ 700060
+ ],
+ "p_char_411_tomimi": [
+ "p_char_411_tomimi",
+ "p_char_411_tomimi",
+ "特米米的信物",
+ "MATERIAL",
+ 700061
+ ],
+ "特米米的信物": [
+ "p_char_411_tomimi",
+ "p_char_411_tomimi",
+ "特米米的信物",
+ "MATERIAL",
+ 700061
+ ],
+ "p_char_415_flint": [
+ "p_char_415_flint",
+ "p_char_415_flint",
+ "燧石的信物",
+ "MATERIAL",
+ 700062
+ ],
+ "燧石的信物": [
+ "p_char_415_flint",
+ "p_char_415_flint",
+ "燧石的信物",
+ "MATERIAL",
+ 700062
+ ],
+ "p_char_365_aprl": [
+ "p_char_365_aprl",
+ "p_char_365_aprl",
+ "四月的信物",
+ "MATERIAL",
+ 700063
+ ],
+ "四月的信物": [
+ "p_char_365_aprl",
+ "p_char_365_aprl",
+ "四月的信物",
+ "MATERIAL",
+ 700063
+ ],
+ "p_char_388_mint": [
+ "p_char_388_mint",
+ "p_char_388_mint",
+ "薄绿的信物",
+ "MATERIAL",
+ 700064
+ ],
+ "薄绿的信物": [
+ "p_char_388_mint",
+ "p_char_388_mint",
+ "薄绿的信物",
+ "MATERIAL",
+ 700064
+ ],
+ "p_char_346_aosta": [
+ "p_char_346_aosta",
+ "p_char_346_aosta",
+ "奥斯塔的信物",
+ "MATERIAL",
+ 700065
+ ],
+ "奥斯塔的信物": [
+ "p_char_346_aosta",
+ "p_char_346_aosta",
+ "奥斯塔的信物",
+ "MATERIAL",
+ 700065
+ ],
+ "p_char_265_sophia": [
+ "p_char_265_sophia",
+ "p_char_265_sophia",
+ "鞭刃的信物",
+ "MATERIAL",
+ 700066
+ ],
+ "鞭刃的信物": [
+ "p_char_265_sophia",
+ "p_char_265_sophia",
+ "鞭刃的信物",
+ "MATERIAL",
+ 700066
+ ],
+ "p_char_436_whispr": [
+ "p_char_436_whispr",
+ "p_char_436_whispr",
+ "絮雨的信物",
+ "MATERIAL",
+ 700067
+ ],
+ "絮雨的信物": [
+ "p_char_436_whispr",
+ "p_char_436_whispr",
+ "絮雨的信物",
+ "MATERIAL",
+ 700067
+ ],
+ "p_char_214_kafka": [
+ "p_char_214_kafka",
+ "p_char_214_kafka",
+ "卡夫卡的信物",
+ "MATERIAL",
+ 700068
+ ],
+ "卡夫卡的信物": [
+ "p_char_214_kafka",
+ "p_char_214_kafka",
+ "卡夫卡的信物",
+ "MATERIAL",
+ 700068
+ ],
+ "p_char_451_robin": [
+ "p_char_451_robin",
+ "p_char_451_robin",
+ "罗宾的信物",
+ "MATERIAL",
+ 700069
+ ],
+ "罗宾的信物": [
+ "p_char_451_robin",
+ "p_char_451_robin",
+ "罗宾的信物",
+ "MATERIAL",
+ 700069
+ ],
+ "p_char_338_iris": [
+ "p_char_338_iris",
+ "p_char_338_iris",
+ "爱丽丝的信物",
+ "MATERIAL",
+ 700070
+ ],
+ "爱丽丝的信物": [
+ "p_char_338_iris",
+ "p_char_338_iris",
+ "爱丽丝的信物",
+ "MATERIAL",
+ 700070
+ ],
+ "p_char_402_tuye": [
+ "p_char_402_tuye",
+ "p_char_402_tuye",
+ "图耶的信物",
+ "MATERIAL",
+ 700071
+ ],
+ "图耶的信物": [
+ "p_char_402_tuye",
+ "p_char_402_tuye",
+ "图耶的信物",
+ "MATERIAL",
+ 700071
+ ],
+ "p_char_455_nothin": [
+ "p_char_455_nothin",
+ "p_char_455_nothin",
+ "乌有的信物",
+ "MATERIAL",
+ 700072
+ ],
+ "乌有的信物": [
+ "p_char_455_nothin",
+ "p_char_455_nothin",
+ "乌有的信物",
+ "MATERIAL",
+ 700072
+ ],
+ "p_char_1011_lava2": [
+ "p_char_1011_lava2",
+ "p_char_1011_lava2",
+ "炎狱炎熔的信物",
+ "MATERIAL",
+ 700073
+ ],
+ "炎狱炎熔的信物": [
+ "p_char_1011_lava2",
+ "p_char_1011_lava2",
+ "炎狱炎熔的信物",
+ "MATERIAL",
+ 700073
+ ],
+ "p_char_457_blitz": [
+ "p_char_457_blitz",
+ "p_char_457_blitz",
+ "闪击的信物",
+ "MATERIAL",
+ 700074
+ ],
+ "闪击的信物": [
+ "p_char_457_blitz",
+ "p_char_457_blitz",
+ "闪击的信物",
+ "MATERIAL",
+ 700074
+ ],
+ "p_char_458_rfrost": [
+ "p_char_458_rfrost",
+ "p_char_458_rfrost",
+ "霜华的信物",
+ "MATERIAL",
+ 700075
+ ],
+ "霜华的信物": [
+ "p_char_458_rfrost",
+ "p_char_458_rfrost",
+ "霜华的信物",
+ "MATERIAL",
+ 700075
+ ],
+ "p_char_459_tachak": [
+ "p_char_459_tachak",
+ "p_char_459_tachak",
+ "战车的信物",
+ "MATERIAL",
+ 700076
+ ],
+ "战车的信物": [
+ "p_char_459_tachak",
+ "p_char_459_tachak",
+ "战车的信物",
+ "MATERIAL",
+ 700076
+ ],
+ "p_char_363_toddi": [
+ "p_char_363_toddi",
+ "p_char_363_toddi",
+ "熔泉的信物",
+ "MATERIAL",
+ 700077
+ ],
+ "熔泉的信物": [
+ "p_char_363_toddi",
+ "p_char_363_toddi",
+ "熔泉的信物",
+ "MATERIAL",
+ 700077
+ ],
+ "p_char_304_zebra": [
+ "p_char_304_zebra",
+ "p_char_304_zebra",
+ "暴雨的信物",
+ "MATERIAL",
+ 700078
+ ],
+ "暴雨的信物": [
+ "p_char_304_zebra",
+ "p_char_304_zebra",
+ "暴雨的信物",
+ "MATERIAL",
+ 700078
+ ],
+ "p_char_475_akafyu": [
+ "p_char_475_akafyu",
+ "p_char_475_akafyu",
+ "赤冬的信物",
+ "MATERIAL",
+ 700079
+ ],
+ "赤冬的信物": [
+ "p_char_475_akafyu",
+ "p_char_475_akafyu",
+ "赤冬的信物",
+ "MATERIAL",
+ 700079
+ ],
+ "p_char_478_kirara": [
+ "p_char_478_kirara",
+ "p_char_478_kirara",
+ "绮良的信物",
+ "MATERIAL",
+ 700080
+ ],
+ "绮良的信物": [
+ "p_char_478_kirara",
+ "p_char_478_kirara",
+ "绮良的信物",
+ "MATERIAL",
+ 700080
+ ],
+ "p_char_369_bena": [
+ "p_char_369_bena",
+ "p_char_369_bena",
+ "贝娜的信物",
+ "MATERIAL",
+ 700081
+ ],
+ "贝娜的信物": [
+ "p_char_369_bena",
+ "p_char_369_bena",
+ "贝娜的信物",
+ "MATERIAL",
+ 700081
+ ],
+ "p_char_421_crow": [
+ "p_char_421_crow",
+ "p_char_421_crow",
+ "羽毛笔的信物",
+ "MATERIAL",
+ 700082
+ ],
+ "羽毛笔的信物": [
+ "p_char_421_crow",
+ "p_char_421_crow",
+ "羽毛笔的信物",
+ "MATERIAL",
+ 700082
+ ],
+ "p_char_486_takila": [
+ "p_char_486_takila",
+ "p_char_486_takila",
+ "龙舌兰的信物",
+ "MATERIAL",
+ 700083
+ ],
+ "龙舌兰的信物": [
+ "p_char_486_takila",
+ "p_char_486_takila",
+ "龙舌兰的信物",
+ "MATERIAL",
+ 700083
+ ],
+ "p_char_473_mberry": [
+ "p_char_473_mberry",
+ "p_char_473_mberry",
+ "桑葚的信物",
+ "MATERIAL",
+ 700084
+ ],
+ "桑葚的信物": [
+ "p_char_473_mberry",
+ "p_char_473_mberry",
+ "桑葚的信物",
+ "MATERIAL",
+ 700084
+ ],
+ "p_char_431_ashlok": [
+ "p_char_431_ashlok",
+ "p_char_431_ashlok",
+ "灰毫的信物",
+ "MATERIAL",
+ 700085
+ ],
+ "灰毫的信物": [
+ "p_char_431_ashlok",
+ "p_char_431_ashlok",
+ "灰毫的信物",
+ "MATERIAL",
+ 700085
+ ],
+ "p_char_449_glider": [
+ "p_char_449_glider",
+ "p_char_449_glider",
+ "蜜莓的信物",
+ "MATERIAL",
+ 700086
+ ],
+ "蜜莓的信物": [
+ "p_char_449_glider",
+ "p_char_449_glider",
+ "蜜莓的信物",
+ "MATERIAL",
+ 700086
+ ],
+ "p_char_496_wildmn": [
+ "p_char_496_wildmn",
+ "p_char_496_wildmn",
+ "野鬃的信物",
+ "MATERIAL",
+ 700087
+ ],
+ "野鬃的信物": [
+ "p_char_496_wildmn",
+ "p_char_496_wildmn",
+ "野鬃的信物",
+ "MATERIAL",
+ 700087
+ ],
+ "p_char_489_serum": [
+ "p_char_489_serum",
+ "p_char_489_serum",
+ "蚀清的信物",
+ "MATERIAL",
+ 700088
+ ],
+ "蚀清的信物": [
+ "p_char_489_serum",
+ "p_char_489_serum",
+ "蚀清的信物",
+ "MATERIAL",
+ 700088
+ ],
+ "p_char_422_aurora": [
+ "p_char_422_aurora",
+ "p_char_422_aurora",
+ "极光的信物",
+ "MATERIAL",
+ 700089
+ ],
+ "极光的信物": [
+ "p_char_422_aurora",
+ "p_char_422_aurora",
+ "极光的信物",
+ "MATERIAL",
+ 700089
+ ],
+ "p_char_4013_kjera": [
+ "p_char_4013_kjera",
+ "p_char_4013_kjera",
+ "耶拉的信物",
+ "MATERIAL",
+ 700090
+ ],
+ "耶拉的信物": [
+ "p_char_4013_kjera",
+ "p_char_4013_kjera",
+ "耶拉的信物",
+ "MATERIAL",
+ 700090
+ ],
+ "p_char_4025_aprot2": [
+ "p_char_4025_aprot2",
+ "p_char_4025_aprot2",
+ "暮落的信物",
+ "MATERIAL",
+ 700091
+ ],
+ "暮落的信物": [
+ "p_char_4025_aprot2",
+ "p_char_4025_aprot2",
+ "暮落的信物",
+ "MATERIAL",
+ 700091
+ ],
+ "p_char_476_blkngt": [
+ "p_char_476_blkngt",
+ "p_char_476_blkngt",
+ "夜半的信物",
+ "MATERIAL",
+ 700092
+ ],
+ "夜半的信物": [
+ "p_char_476_blkngt",
+ "p_char_476_blkngt",
+ "夜半的信物",
+ "MATERIAL",
+ 700092
+ ],
+ "p_char_492_quercu": [
+ "p_char_492_quercu",
+ "p_char_492_quercu",
+ "夏栎的信物",
+ "MATERIAL",
+ 700093
+ ],
+ "夏栎的信物": [
+ "p_char_492_quercu",
+ "p_char_492_quercu",
+ "夏栎的信物",
+ "MATERIAL",
+ 700093
+ ],
+ "p_char_1021_kroos2": [
+ "p_char_1021_kroos2",
+ "p_char_1021_kroos2",
+ "寒芒克洛丝的信物",
+ "MATERIAL",
+ 700094
+ ],
+ "寒芒克洛丝的信物": [
+ "p_char_1021_kroos2",
+ "p_char_1021_kroos2",
+ "寒芒克洛丝的信物",
+ "MATERIAL",
+ 700094
+ ],
+ "p_char_4016_kazema": [
+ "p_char_4016_kazema",
+ "p_char_4016_kazema",
+ "风丸的信物",
+ "MATERIAL",
+ 700095
+ ],
+ "风丸的信物": [
+ "p_char_4016_kazema",
+ "p_char_4016_kazema",
+ "风丸的信物",
+ "MATERIAL",
+ 700095
+ ],
+ "p_char_4036_forcer": [
+ "p_char_4036_forcer",
+ "p_char_4036_forcer",
+ "见行者的信物",
+ "MATERIAL",
+ 700096
+ ],
+ "见行者的信物": [
+ "p_char_4036_forcer",
+ "p_char_4036_forcer",
+ "见行者的信物",
+ "MATERIAL",
+ 700096
+ ],
+ "p_char_4040_rockr": [
+ "p_char_4040_rockr",
+ "p_char_4040_rockr",
+ "洛洛的信物",
+ "MATERIAL",
+ 700097
+ ],
+ "洛洛的信物": [
+ "p_char_4040_rockr",
+ "p_char_4040_rockr",
+ "洛洛的信物",
+ "MATERIAL",
+ 700097
+ ],
+ "p_char_4045_heidi": [
+ "p_char_4045_heidi",
+ "p_char_4045_heidi",
+ "海蒂的信物",
+ "MATERIAL",
+ 700098
+ ],
+ "海蒂的信物": [
+ "p_char_4045_heidi",
+ "p_char_4045_heidi",
+ "海蒂的信物",
+ "MATERIAL",
+ 700098
+ ],
+ "p_char_433_windft": [
+ "p_char_433_windft",
+ "p_char_433_windft",
+ "掠风的信物",
+ "MATERIAL",
+ 700099
+ ],
+ "掠风的信物": [
+ "p_char_433_windft",
+ "p_char_433_windft",
+ "掠风的信物",
+ "MATERIAL",
+ 700099
+ ],
+ "p_char_4043_erato": [
+ "p_char_4043_erato",
+ "p_char_4043_erato",
+ "埃拉托的信物",
+ "MATERIAL",
+ 700100
+ ],
+ "埃拉托的信物": [
+ "p_char_4043_erato",
+ "p_char_4043_erato",
+ "埃拉托的信物",
+ "MATERIAL",
+ 700100
+ ],
+ "p_char_1024_hbisc2": [
+ "p_char_1024_hbisc2",
+ "p_char_1024_hbisc2",
+ "濯尘芙蓉的信物",
+ "MATERIAL",
+ 700101
+ ],
+ "濯尘芙蓉的信物": [
+ "p_char_1024_hbisc2",
+ "p_char_1024_hbisc2",
+ "濯尘芙蓉的信物",
+ "MATERIAL",
+ 700101
+ ],
+ "p_char_4047_pianst": [
+ "p_char_4047_pianst",
+ "p_char_4047_pianst",
+ "车尔尼的信物",
+ "MATERIAL",
+ 700102
+ ],
+ "车尔尼的信物": [
+ "p_char_4047_pianst",
+ "p_char_4047_pianst",
+ "车尔尼的信物",
+ "MATERIAL",
+ 700102
+ ],
+ "p_char_1027_greyy2": [
+ "p_char_1027_greyy2",
+ "p_char_1027_greyy2",
+ "承曦格雷伊的信物",
+ "MATERIAL",
+ 700103
+ ],
+ "承曦格雷伊的信物": [
+ "p_char_1027_greyy2",
+ "p_char_1027_greyy2",
+ "承曦格雷伊的信物",
+ "MATERIAL",
+ 700103
+ ],
+ "p_char_135_halo": [
+ "p_char_135_halo",
+ "p_char_135_halo",
+ "星源的信物",
+ "MATERIAL",
+ 700104
+ ],
+ "星源的信物": [
+ "p_char_135_halo",
+ "p_char_135_halo",
+ "星源的信物",
+ "MATERIAL",
+ 700104
+ ],
+ "p_char_497_ctable": [
+ "p_char_497_ctable",
+ "p_char_497_ctable",
+ "晓歌的信物",
+ "MATERIAL",
+ 700105
+ ],
+ "晓歌的信物": [
+ "p_char_497_ctable",
+ "p_char_497_ctable",
+ "晓歌的信物",
+ "MATERIAL",
+ 700105
+ ],
+ "p_char_4054_malist": [
+ "p_char_4054_malist",
+ "p_char_4054_malist",
+ "至简的信物",
+ "MATERIAL",
+ 700106
+ ],
+ "至简的信物": [
+ "p_char_4054_malist",
+ "p_char_4054_malist",
+ "至简的信物",
+ "MATERIAL",
+ 700106
+ ],
+ "p_char_4032_provs": [
+ "p_char_4032_provs",
+ "p_char_4032_provs",
+ "但书的信物",
+ "MATERIAL",
+ 700107
+ ],
+ "但书的信物": [
+ "p_char_4032_provs",
+ "p_char_4032_provs",
+ "但书的信物",
+ "MATERIAL",
+ 700107
+ ],
+ "p_char_4066_highmo": [
+ "p_char_4066_highmo",
+ "p_char_4066_highmo",
+ "海沫的信物",
+ "MATERIAL",
+ 700108
+ ],
+ "海沫的信物": [
+ "p_char_4066_highmo",
+ "p_char_4066_highmo",
+ "海沫的信物",
+ "MATERIAL",
+ 700108
+ ],
+ "p_char_4071_peper": [
+ "p_char_4071_peper",
+ "p_char_4071_peper",
+ "明椒的信物",
+ "MATERIAL",
+ 700109
+ ],
+ "明椒的信物": [
+ "p_char_4071_peper",
+ "p_char_4071_peper",
+ "明椒的信物",
+ "MATERIAL",
+ 700109
+ ],
+ "p_char_157_dagda": [
+ "p_char_157_dagda",
+ "p_char_157_dagda",
+ "达格达的信物",
+ "MATERIAL",
+ 700110
+ ],
+ "达格达的信物": [
+ "p_char_157_dagda",
+ "p_char_157_dagda",
+ "达格达的信物",
+ "MATERIAL",
+ 700110
+ ],
+ "p_char_466_qanik": [
+ "p_char_466_qanik",
+ "p_char_466_qanik",
+ "雪绒的信物",
+ "MATERIAL",
+ 700111
+ ],
+ "雪绒的信物": [
+ "p_char_466_qanik",
+ "p_char_466_qanik",
+ "雪绒的信物",
+ "MATERIAL",
+ 700111
+ ],
+ "p_char_4014_lunacu": [
+ "p_char_4014_lunacu",
+ "p_char_4014_lunacu",
+ "子月的信物",
+ "MATERIAL",
+ 700112
+ ],
+ "子月的信物": [
+ "p_char_4014_lunacu",
+ "p_char_4014_lunacu",
+ "子月的信物",
+ "MATERIAL",
+ 700112
+ ],
+ "p_char_297_hamoni": [
+ "p_char_297_hamoni",
+ "p_char_297_hamoni",
+ "和弦的信物",
+ "MATERIAL",
+ 700113
+ ],
+ "和弦的信物": [
+ "p_char_297_hamoni",
+ "p_char_297_hamoni",
+ "和弦的信物",
+ "MATERIAL",
+ 700113
+ ],
+ "p_char_4017_puzzle": [
+ "p_char_4017_puzzle",
+ "p_char_4017_puzzle",
+ "谜图的信物",
+ "MATERIAL",
+ 700114
+ ],
+ "谜图的信物": [
+ "p_char_4017_puzzle",
+ "p_char_4017_puzzle",
+ "谜图的信物",
+ "MATERIAL",
+ 700114
+ ],
+ "p_char_4078_bdhkgt": [
+ "p_char_4078_bdhkgt",
+ "p_char_4078_bdhkgt",
+ "截云的信物",
+ "MATERIAL",
+ 700115
+ ],
+ "截云的信物": [
+ "p_char_4078_bdhkgt",
+ "p_char_4078_bdhkgt",
+ "截云的信物",
+ "MATERIAL",
+ 700115
+ ],
+ "p_char_493_firwhl": [
+ "p_char_493_firwhl",
+ "p_char_493_firwhl",
+ "火哨的信物",
+ "MATERIAL",
+ 700116
+ ],
+ "火哨的信物": [
+ "p_char_493_firwhl",
+ "p_char_493_firwhl",
+ "火哨的信物",
+ "MATERIAL",
+ 700116
+ ],
+ "p_char_4083_chimes": [
+ "p_char_4083_chimes",
+ "p_char_4083_chimes",
+ "铎铃的信物",
+ "MATERIAL",
+ 700117
+ ],
+ "铎铃的信物": [
+ "p_char_4083_chimes",
+ "p_char_4083_chimes",
+ "铎铃的信物",
+ "MATERIAL",
+ 700117
+ ],
+ "p_char_1030_noirc2": [
+ "p_char_1030_noirc2",
+ "p_char_1030_noirc2",
+ "火龙S黑角的信物",
+ "MATERIAL",
+ 700118
+ ],
+ "火龙S黑角的信物": [
+ "p_char_1030_noirc2",
+ "p_char_1030_noirc2",
+ "火龙S黑角的信物",
+ "MATERIAL",
+ 700118
+ ],
+ "p_char_154_morgan": [
+ "p_char_154_morgan",
+ "p_char_154_morgan",
+ "摩根的信物",
+ "MATERIAL",
+ 700119
+ ],
+ "摩根的信物": [
+ "p_char_154_morgan",
+ "p_char_154_morgan",
+ "摩根的信物",
+ "MATERIAL",
+ 700119
+ ],
+ "p_char_464_cement": [
+ "p_char_464_cement",
+ "p_char_464_cement",
+ "洋灰的信物",
+ "MATERIAL",
+ 700120
+ ],
+ "洋灰的信物": [
+ "p_char_464_cement",
+ "p_char_464_cement",
+ "洋灰的信物",
+ "MATERIAL",
+ 700120
+ ],
+ "p_char_4006_melnte": [
+ "p_char_4006_melnte",
+ "p_char_4006_melnte",
+ "玫拉的信物",
+ "MATERIAL",
+ 700121
+ ],
+ "玫拉的信物": [
+ "p_char_4006_melnte",
+ "p_char_4006_melnte",
+ "玫拉的信物",
+ "MATERIAL",
+ 700121
+ ],
+ "p_char_498_inside": [
+ "p_char_498_inside",
+ "p_char_498_inside",
+ "隐现的信物",
+ "MATERIAL",
+ 700122
+ ],
+ "隐现的信物": [
+ "p_char_498_inside",
+ "p_char_498_inside",
+ "隐现的信物",
+ "MATERIAL",
+ 700122
+ ],
+ "p_char_4015_spuria": [
+ "p_char_4015_spuria",
+ "p_char_4015_spuria",
+ "空构的信物",
+ "MATERIAL",
+ 700123
+ ],
+ "空构的信物": [
+ "p_char_4015_spuria",
+ "p_char_4015_spuria",
+ "空构的信物",
+ "MATERIAL",
+ 700123
+ ],
+ "p_char_341_sntlla": [
+ "p_char_341_sntlla",
+ "p_char_341_sntlla",
+ "寒檀的信物",
+ "MATERIAL",
+ 700124
+ ],
+ "寒檀的信物": [
+ "p_char_341_sntlla",
+ "p_char_341_sntlla",
+ "寒檀的信物",
+ "MATERIAL",
+ 700124
+ ],
+ "p_char_4102_threye": [
+ "p_char_4102_threye",
+ "p_char_4102_threye",
+ "凛视的信物",
+ "MATERIAL",
+ 700125
+ ],
+ "凛视的信物": [
+ "p_char_4102_threye",
+ "p_char_4102_threye",
+ "凛视的信物",
+ "MATERIAL",
+ 700125
+ ],
+ "p_char_4106_bryota": [
+ "p_char_4106_bryota",
+ "p_char_4106_bryota",
+ "苍苔的信物",
+ "MATERIAL",
+ 700126
+ ],
+ "苍苔的信物": [
+ "p_char_4106_bryota",
+ "p_char_4106_bryota",
+ "苍苔的信物",
+ "MATERIAL",
+ 700126
+ ],
+ "p_char_488_buildr": [
+ "p_char_488_buildr",
+ "p_char_488_buildr",
+ "青枳的信物",
+ "MATERIAL",
+ 700127
+ ],
+ "青枳的信物": [
+ "p_char_488_buildr",
+ "p_char_488_buildr",
+ "青枳的信物",
+ "MATERIAL",
+ 700127
+ ],
+ "p_char_4105_almond": [
+ "p_char_4105_almond",
+ "p_char_4105_almond",
+ "杏仁的信物",
+ "MATERIAL",
+ 700128
+ ],
+ "杏仁的信物": [
+ "p_char_4105_almond",
+ "p_char_4105_almond",
+ "杏仁的信物",
+ "MATERIAL",
+ 700128
+ ],
+ "p_char_4104_coldst": [
+ "p_char_4104_coldst",
+ "p_char_4104_coldst",
+ "冰酿的信物",
+ "MATERIAL",
+ 700129
+ ],
+ "冰酿的信物": [
+ "p_char_4104_coldst",
+ "p_char_4104_coldst",
+ "冰酿的信物",
+ "MATERIAL",
+ 700129
+ ],
+ "p_char_4110_delphn": [
+ "p_char_4110_delphn",
+ "p_char_4110_delphn",
+ "戴菲恩的信物",
+ "MATERIAL",
+ 700130
+ ],
+ "戴菲恩的信物": [
+ "p_char_4110_delphn",
+ "p_char_4110_delphn",
+ "戴菲恩的信物",
+ "MATERIAL",
+ 700130
+ ],
+ "p_char_494_vendla": [
+ "p_char_494_vendla",
+ "p_char_494_vendla",
+ "刺玫的信物",
+ "MATERIAL",
+ 700131
+ ],
+ "刺玫的信物": [
+ "p_char_494_vendla",
+ "p_char_494_vendla",
+ "刺玫的信物",
+ "MATERIAL",
+ 700131
+ ],
+ "p_char_499_kaitou": [
+ "p_char_499_kaitou",
+ "p_char_499_kaitou",
+ "折光的信物",
+ "MATERIAL",
+ 700132
+ ],
+ "折光的信物": [
+ "p_char_499_kaitou",
+ "p_char_499_kaitou",
+ "折光的信物",
+ "MATERIAL",
+ 700132
+ ],
+ "p_char_4109_baslin": [
+ "p_char_4109_baslin",
+ "p_char_4109_baslin",
+ "深律的信物",
+ "MATERIAL",
+ 700133
+ ],
+ "深律的信物": [
+ "p_char_4109_baslin",
+ "p_char_4109_baslin",
+ "深律的信物",
+ "MATERIAL",
+ 700133
+ ],
+ "p_char_194_leto": [
+ "p_char_194_leto",
+ "p_char_194_leto",
+ "烈夏的信物",
+ "MATERIAL",
+ 700134
+ ],
+ "烈夏的信物": [
+ "p_char_194_leto",
+ "p_char_194_leto",
+ "烈夏的信物",
+ "MATERIAL",
+ 700134
+ ],
+ "p_char_4114_harold": [
+ "p_char_4114_harold",
+ "p_char_4114_harold",
+ "哈洛德的信物",
+ "MATERIAL",
+ 700135
+ ],
+ "哈洛德的信物": [
+ "p_char_4114_harold",
+ "p_char_4114_harold",
+ "哈洛德的信物",
+ "MATERIAL",
+ 700135
+ ],
+ "p_char_4081_warmy": [
+ "p_char_4081_warmy",
+ "p_char_4081_warmy",
+ "温米的信物",
+ "MATERIAL",
+ 700136
+ ],
+ "温米的信物": [
+ "p_char_4081_warmy",
+ "p_char_4081_warmy",
+ "温米的信物",
+ "MATERIAL",
+ 700136
+ ],
+ "p_char_4119_wanqin": [
+ "p_char_4119_wanqin",
+ "p_char_4119_wanqin",
+ "万顷的信物",
+ "MATERIAL",
+ 700137
+ ],
+ "万顷的信物": [
+ "p_char_4119_wanqin",
+ "p_char_4119_wanqin",
+ "万顷的信物",
+ "MATERIAL",
+ 700137
+ ],
+ "p_char_4122_grabds": [
+ "p_char_4122_grabds",
+ "p_char_4122_grabds",
+ "小满的信物",
+ "MATERIAL",
+ 700138
+ ],
+ "小满的信物": [
+ "p_char_4122_grabds",
+ "p_char_4122_grabds",
+ "小满的信物",
+ "MATERIAL",
+ 700138
+ ],
+ "p_char_4023_rfalcn": [
+ "p_char_4023_rfalcn",
+ "p_char_4023_rfalcn",
+ "红隼的信物",
+ "MATERIAL",
+ 700139
+ ],
+ "红隼的信物": [
+ "p_char_4023_rfalcn",
+ "p_char_4023_rfalcn",
+ "红隼的信物",
+ "MATERIAL",
+ 700139
+ ],
+ "p_char_4124_iana": [
+ "p_char_4124_iana",
+ "p_char_4124_iana",
+ "双月的信物",
+ "MATERIAL",
+ 700140
+ ],
+ "双月的信物": [
+ "p_char_4124_iana",
+ "p_char_4124_iana",
+ "双月的信物",
+ "MATERIAL",
+ 700140
+ ],
+ "p_char_4125_rdoc": [
+ "p_char_4125_rdoc",
+ "p_char_4125_rdoc",
+ "医生的信物",
+ "MATERIAL",
+ 700141
+ ],
+ "医生的信物": [
+ "p_char_4125_rdoc",
+ "p_char_4125_rdoc",
+ "医生的信物",
+ "MATERIAL",
+ 700141
+ ],
+ "p_char_4126_fuze": [
+ "p_char_4126_fuze",
+ "p_char_4126_fuze",
+ "导火索的信物",
+ "MATERIAL",
+ 700142
+ ],
+ "导火索的信物": [
+ "p_char_4126_fuze",
+ "p_char_4126_fuze",
+ "导火索的信物",
+ "MATERIAL",
+ 700142
+ ],
+ "p_char_446_aroma": [
+ "p_char_446_aroma",
+ "p_char_446_aroma",
+ "阿罗玛的信物",
+ "MATERIAL",
+ 700143
+ ],
+ "阿罗玛的信物": [
+ "p_char_446_aroma",
+ "p_char_446_aroma",
+ "阿罗玛的信物",
+ "MATERIAL",
+ 700143
+ ],
+ "p_char_4131_odda": [
+ "p_char_4131_odda",
+ "p_char_4131_odda",
+ "奥达的信物",
+ "MATERIAL",
+ 700144
+ ],
+ "奥达的信物": [
+ "p_char_4131_odda",
+ "p_char_4131_odda",
+ "奥达的信物",
+ "MATERIAL",
+ 700144
+ ],
+ "p_char_1036_fang2": [
+ "p_char_1036_fang2",
+ "p_char_1036_fang2",
+ "历阵锐枪芬的信物",
+ "MATERIAL",
+ 700145
+ ],
+ "历阵锐枪芬的信物": [
+ "p_char_1036_fang2",
+ "p_char_1036_fang2",
+ "历阵锐枪芬的信物",
+ "MATERIAL",
+ 700145
+ ],
+ "p_char_4079_haini": [
+ "p_char_4079_haini",
+ "p_char_4079_haini",
+ "海霓的信物",
+ "MATERIAL",
+ 700146
+ ],
+ "海霓的信物": [
+ "p_char_4079_haini",
+ "p_char_4079_haini",
+ "海霓的信物",
+ "MATERIAL",
+ 700146
+ ],
+ "p_char_4137_udflow": [
+ "p_char_4137_udflow",
+ "p_char_4137_udflow",
+ "深巡的信物",
+ "MATERIAL",
+ 700147
+ ],
+ "深巡的信物": [
+ "p_char_4137_udflow",
+ "p_char_4137_udflow",
+ "深巡的信物",
+ "MATERIAL",
+ 700147
+ ],
+ "p_char_4147_mitm": [
+ "p_char_4147_mitm",
+ "p_char_4147_mitm",
+ "渡桥的信物",
+ "MATERIAL",
+ 700148
+ ],
+ "渡桥的信物": [
+ "p_char_4147_mitm",
+ "p_char_4147_mitm",
+ "渡桥的信物",
+ "MATERIAL",
+ 700148
+ ],
+ "p_char_4151_tinman": [
+ "p_char_4151_tinman",
+ "p_char_4151_tinman",
+ "锡人的信物",
+ "MATERIAL",
+ 700149
+ ],
+ "锡人的信物": [
+ "p_char_4151_tinman",
+ "p_char_4151_tinman",
+ "锡人的信物",
+ "MATERIAL",
+ 700149
+ ],
+ "p_char_4140_lasher": [
+ "p_char_4140_lasher",
+ "p_char_4140_lasher",
+ "衡沙的信物",
+ "MATERIAL",
+ 700150
+ ],
+ "衡沙的信物": [
+ "p_char_4140_lasher",
+ "p_char_4140_lasher",
+ "衡沙的信物",
+ "MATERIAL",
+ 700150
+ ],
+ "p_char_4139_papyrs": [
+ "p_char_4139_papyrs",
+ "p_char_4139_papyrs",
+ "莎草的信物",
+ "MATERIAL",
+ 700151
+ ],
+ "莎草的信物": [
+ "p_char_4139_papyrs",
+ "p_char_4139_papyrs",
+ "莎草的信物",
+ "MATERIAL",
+ 700151
+ ],
+ "p_char_4142_laios": [
+ "p_char_4142_laios",
+ "p_char_4142_laios",
+ "莱欧斯的信物",
+ "MATERIAL",
+ 700152
+ ],
+ "莱欧斯的信物": [
+ "p_char_4142_laios",
+ "p_char_4142_laios",
+ "莱欧斯的信物",
+ "MATERIAL",
+ 700152
+ ],
+ "p_char_4143_sensi": [
+ "p_char_4143_sensi",
+ "p_char_4143_sensi",
+ "森西的信物",
+ "MATERIAL",
+ 700153
+ ],
+ "森西的信物": [
+ "p_char_4143_sensi",
+ "p_char_4143_sensi",
+ "森西的信物",
+ "MATERIAL",
+ 700153
+ ],
+ "p_char_4144_chilc": [
+ "p_char_4144_chilc",
+ "p_char_4144_chilc",
+ "齐尔查克的信物",
+ "MATERIAL",
+ 700154
+ ],
+ "齐尔查克的信物": [
+ "p_char_4144_chilc",
+ "p_char_4144_chilc",
+ "齐尔查克的信物",
+ "MATERIAL",
+ 700154
+ ],
+ "p_char_103_angel": [
+ "p_char_103_angel",
+ "p_char_103_angel",
+ "能天使的信物",
+ "MATERIAL",
+ 600001
+ ],
+ "能天使的信物": [
+ "p_char_103_angel",
+ "p_char_103_angel",
+ "能天使的信物",
+ "MATERIAL",
+ 600001
+ ],
+ "p_char_112_siege": [
+ "p_char_112_siege",
+ "p_char_112_siege",
+ "推进之王的信物",
+ "MATERIAL",
+ 600002
+ ],
+ "推进之王的信物": [
+ "p_char_112_siege",
+ "p_char_112_siege",
+ "推进之王的信物",
+ "MATERIAL",
+ 600002
+ ],
+ "p_char_134_ifrit": [
+ "p_char_134_ifrit",
+ "p_char_134_ifrit",
+ "伊芙利特的信物",
+ "MATERIAL",
+ 600003
+ ],
+ "伊芙利特的信物": [
+ "p_char_134_ifrit",
+ "p_char_134_ifrit",
+ "伊芙利特的信物",
+ "MATERIAL",
+ 600003
+ ],
+ "p_char_180_amgoat": [
+ "p_char_180_amgoat",
+ "p_char_180_amgoat",
+ "艾雅法拉的信物",
+ "MATERIAL",
+ 600004
+ ],
+ "艾雅法拉的信物": [
+ "p_char_180_amgoat",
+ "p_char_180_amgoat",
+ "艾雅法拉的信物",
+ "MATERIAL",
+ 600004
+ ],
+ "p_char_291_aglina": [
+ "p_char_291_aglina",
+ "p_char_291_aglina",
+ "安洁莉娜的信物",
+ "MATERIAL",
+ 600005
+ ],
+ "安洁莉娜的信物": [
+ "p_char_291_aglina",
+ "p_char_291_aglina",
+ "安洁莉娜的信物",
+ "MATERIAL",
+ 600005
+ ],
+ "p_char_147_shining": [
+ "p_char_147_shining",
+ "p_char_147_shining",
+ "闪灵的信物",
+ "MATERIAL",
+ 600006
+ ],
+ "闪灵的信物": [
+ "p_char_147_shining",
+ "p_char_147_shining",
+ "闪灵的信物",
+ "MATERIAL",
+ 600006
+ ],
+ "p_char_179_cgbird": [
+ "p_char_179_cgbird",
+ "p_char_179_cgbird",
+ "夜莺的信物",
+ "MATERIAL",
+ 600007
+ ],
+ "夜莺的信物": [
+ "p_char_179_cgbird",
+ "p_char_179_cgbird",
+ "夜莺的信物",
+ "MATERIAL",
+ 600007
+ ],
+ "p_char_136_hsguma": [
+ "p_char_136_hsguma",
+ "p_char_136_hsguma",
+ "星熊的信物",
+ "MATERIAL",
+ 600008
+ ],
+ "星熊的信物": [
+ "p_char_136_hsguma",
+ "p_char_136_hsguma",
+ "星熊的信物",
+ "MATERIAL",
+ 600008
+ ],
+ "p_char_202_demkni": [
+ "p_char_202_demkni",
+ "p_char_202_demkni",
+ "塞雷娅的信物",
+ "MATERIAL",
+ 600009
+ ],
+ "塞雷娅的信物": [
+ "p_char_202_demkni",
+ "p_char_202_demkni",
+ "塞雷娅的信物",
+ "MATERIAL",
+ 600009
+ ],
+ "p_char_172_svrash": [
+ "p_char_172_svrash",
+ "p_char_172_svrash",
+ "银灰的信物",
+ "MATERIAL",
+ 600010
+ ],
+ "银灰的信物": [
+ "p_char_172_svrash",
+ "p_char_172_svrash",
+ "银灰的信物",
+ "MATERIAL",
+ 600010
+ ],
+ "p_char_263_skadi": [
+ "p_char_263_skadi",
+ "p_char_263_skadi",
+ "斯卡蒂的信物",
+ "MATERIAL",
+ 600011
+ ],
+ "斯卡蒂的信物": [
+ "p_char_263_skadi",
+ "p_char_263_skadi",
+ "斯卡蒂的信物",
+ "MATERIAL",
+ 600011
+ ],
+ "p_char_010_chen": [
+ "p_char_010_chen",
+ "p_char_010_chen",
+ "陈的信物",
+ "MATERIAL",
+ 600012
+ ],
+ "陈的信物": [
+ "p_char_010_chen",
+ "p_char_010_chen",
+ "陈的信物",
+ "MATERIAL",
+ 600012
+ ],
+ "p_char_340_shwaz": [
+ "p_char_340_shwaz",
+ "p_char_340_shwaz",
+ "黑的信物",
+ "MATERIAL",
+ 600013
+ ],
+ "黑的信物": [
+ "p_char_340_shwaz",
+ "p_char_340_shwaz",
+ "黑的信物",
+ "MATERIAL",
+ 600013
+ ],
+ "p_char_188_helage": [
+ "p_char_188_helage",
+ "p_char_188_helage",
+ "赫拉格的信物",
+ "MATERIAL",
+ 600014
+ ],
+ "赫拉格的信物": [
+ "p_char_188_helage",
+ "p_char_188_helage",
+ "赫拉格的信物",
+ "MATERIAL",
+ 600014
+ ],
+ "p_char_248_mgllan": [
+ "p_char_248_mgllan",
+ "p_char_248_mgllan",
+ "麦哲伦的信物",
+ "MATERIAL",
+ 600015
+ ],
+ "麦哲伦的信物": [
+ "p_char_248_mgllan",
+ "p_char_248_mgllan",
+ "麦哲伦的信物",
+ "MATERIAL",
+ 600015
+ ],
+ "p_char_213_mostma": [
+ "p_char_213_mostma",
+ "p_char_213_mostma",
+ "莫斯提马的信物",
+ "MATERIAL",
+ 600016
+ ],
+ "莫斯提马的信物": [
+ "p_char_213_mostma",
+ "p_char_213_mostma",
+ "莫斯提马的信物",
+ "MATERIAL",
+ 600016
+ ],
+ "p_char_225_haak": [
+ "p_char_225_haak",
+ "p_char_225_haak",
+ "阿的信物",
+ "MATERIAL",
+ 600017
+ ],
+ "阿的信物": [
+ "p_char_225_haak",
+ "p_char_225_haak",
+ "阿的信物",
+ "MATERIAL",
+ 600017
+ ],
+ "p_char_2014_nian": [
+ "p_char_2014_nian",
+ "p_char_2014_nian",
+ "年的信物",
+ "MATERIAL",
+ 600018
+ ],
+ "年的信物": [
+ "p_char_2014_nian",
+ "p_char_2014_nian",
+ "年的信物",
+ "MATERIAL",
+ 600018
+ ],
+ "p_char_017_huang": [
+ "p_char_017_huang",
+ "p_char_017_huang",
+ "煌的信物",
+ "MATERIAL",
+ 600019
+ ],
+ "煌的信物": [
+ "p_char_017_huang",
+ "p_char_017_huang",
+ "煌的信物",
+ "MATERIAL",
+ 600019
+ ],
+ "p_char_2013_cerber": [
+ "p_char_2013_cerber",
+ "p_char_2013_cerber",
+ "刻俄柏的信物",
+ "MATERIAL",
+ 600020
+ ],
+ "刻俄柏的信物": [
+ "p_char_2013_cerber",
+ "p_char_2013_cerber",
+ "刻俄柏的信物",
+ "MATERIAL",
+ 600020
+ ],
+ "p_char_222_bpipe": [
+ "p_char_222_bpipe",
+ "p_char_222_bpipe",
+ "风笛的信物",
+ "MATERIAL",
+ 600021
+ ],
+ "风笛的信物": [
+ "p_char_222_bpipe",
+ "p_char_222_bpipe",
+ "风笛的信物",
+ "MATERIAL",
+ 600021
+ ],
+ "p_char_250_phatom": [
+ "p_char_250_phatom",
+ "p_char_250_phatom",
+ "傀影的信物",
+ "MATERIAL",
+ 600022
+ ],
+ "傀影的信物": [
+ "p_char_250_phatom",
+ "p_char_250_phatom",
+ "傀影的信物",
+ "MATERIAL",
+ 600022
+ ],
+ "p_char_400_weedy": [
+ "p_char_400_weedy",
+ "p_char_400_weedy",
+ "温蒂的信物",
+ "MATERIAL",
+ 600023
+ ],
+ "温蒂的信物": [
+ "p_char_400_weedy",
+ "p_char_400_weedy",
+ "温蒂的信物",
+ "MATERIAL",
+ 600023
+ ],
+ "p_char_113_cqbw": [
+ "p_char_113_cqbw",
+ "p_char_113_cqbw",
+ "W的信物",
+ "MATERIAL",
+ 600024
+ ],
+ "W的信物": [
+ "p_char_113_cqbw",
+ "p_char_113_cqbw",
+ "W的信物",
+ "MATERIAL",
+ 600024
+ ],
+ "p_char_197_poca": [
+ "p_char_197_poca",
+ "p_char_197_poca",
+ "早露的信物",
+ "MATERIAL",
+ 600025
+ ],
+ "早露的信物": [
+ "p_char_197_poca",
+ "p_char_197_poca",
+ "早露的信物",
+ "MATERIAL",
+ 600025
+ ],
+ "p_char_358_lisa": [
+ "p_char_358_lisa",
+ "p_char_358_lisa",
+ "铃兰的信物",
+ "MATERIAL",
+ 600026
+ ],
+ "铃兰的信物": [
+ "p_char_358_lisa",
+ "p_char_358_lisa",
+ "铃兰的信物",
+ "MATERIAL",
+ 600026
+ ],
+ "p_char_293_thorns": [
+ "p_char_293_thorns",
+ "p_char_293_thorns",
+ "棘刺的信物",
+ "MATERIAL",
+ 600027
+ ],
+ "棘刺的信物": [
+ "p_char_293_thorns",
+ "p_char_293_thorns",
+ "棘刺的信物",
+ "MATERIAL",
+ 600027
+ ],
+ "p_char_416_zumama": [
+ "p_char_416_zumama",
+ "p_char_416_zumama",
+ "森蚺的信物",
+ "MATERIAL",
+ 600028
+ ],
+ "森蚺的信物": [
+ "p_char_416_zumama",
+ "p_char_416_zumama",
+ "森蚺的信物",
+ "MATERIAL",
+ 600028
+ ],
+ "p_char_350_surtr": [
+ "p_char_350_surtr",
+ "p_char_350_surtr",
+ "史尔特尔的信物",
+ "MATERIAL",
+ 600029
+ ],
+ "史尔特尔的信物": [
+ "p_char_350_surtr",
+ "p_char_350_surtr",
+ "史尔特尔的信物",
+ "MATERIAL",
+ 600029
+ ],
+ "p_char_423_blemsh": [
+ "p_char_423_blemsh",
+ "p_char_423_blemsh",
+ "瑕光的信物",
+ "MATERIAL",
+ 600030
+ ],
+ "瑕光的信物": [
+ "p_char_423_blemsh",
+ "p_char_423_blemsh",
+ "瑕光的信物",
+ "MATERIAL",
+ 600030
+ ],
+ "p_char_391_rosmon": [
+ "p_char_391_rosmon",
+ "p_char_391_rosmon",
+ "迷迭香的信物",
+ "MATERIAL",
+ 600031
+ ],
+ "迷迭香的信物": [
+ "p_char_391_rosmon",
+ "p_char_391_rosmon",
+ "迷迭香的信物",
+ "MATERIAL",
+ 600031
+ ],
+ "p_char_311_mudrok": [
+ "p_char_311_mudrok",
+ "p_char_311_mudrok",
+ "泥岩的信物",
+ "MATERIAL",
+ 600032
+ ],
+ "泥岩的信物": [
+ "p_char_311_mudrok",
+ "p_char_311_mudrok",
+ "泥岩的信物",
+ "MATERIAL",
+ 600032
+ ],
+ "p_char_264_f12yin": [
+ "p_char_264_f12yin",
+ "p_char_264_f12yin",
+ "山的信物",
+ "MATERIAL",
+ 600033
+ ],
+ "山的信物": [
+ "p_char_264_f12yin",
+ "p_char_264_f12yin",
+ "山的信物",
+ "MATERIAL",
+ 600033
+ ],
+ "p_char_332_archet": [
+ "p_char_332_archet",
+ "p_char_332_archet",
+ "空弦的信物",
+ "MATERIAL",
+ 600034
+ ],
+ "空弦的信物": [
+ "p_char_332_archet",
+ "p_char_332_archet",
+ "空弦的信物",
+ "MATERIAL",
+ 600034
+ ],
+ "p_char_2015_dusk": [
+ "p_char_2015_dusk",
+ "p_char_2015_dusk",
+ "夕的信物",
+ "MATERIAL",
+ 600035
+ ],
+ "夕的信物": [
+ "p_char_2015_dusk",
+ "p_char_2015_dusk",
+ "夕的信物",
+ "MATERIAL",
+ 600035
+ ],
+ "p_char_362_saga": [
+ "p_char_362_saga",
+ "p_char_362_saga",
+ "嵯峨的信物",
+ "MATERIAL",
+ 600036
+ ],
+ "嵯峨的信物": [
+ "p_char_362_saga",
+ "p_char_362_saga",
+ "嵯峨的信物",
+ "MATERIAL",
+ 600036
+ ],
+ "p_char_456_ash": [
+ "p_char_456_ash",
+ "p_char_456_ash",
+ "灰烬的信物",
+ "MATERIAL",
+ 600037
+ ],
+ "灰烬的信物": [
+ "p_char_456_ash",
+ "p_char_456_ash",
+ "灰烬的信物",
+ "MATERIAL",
+ 600037
+ ],
+ "p_char_472_pasngr": [
+ "p_char_472_pasngr",
+ "p_char_472_pasngr",
+ "异客的信物",
+ "MATERIAL",
+ 600038
+ ],
+ "异客的信物": [
+ "p_char_472_pasngr",
+ "p_char_472_pasngr",
+ "异客的信物",
+ "MATERIAL",
+ 600038
+ ],
+ "p_char_1012_skadi2": [
+ "p_char_1012_skadi2",
+ "p_char_1012_skadi2",
+ "浊心斯卡蒂的信物",
+ "MATERIAL",
+ 600039
+ ],
+ "浊心斯卡蒂的信物": [
+ "p_char_1012_skadi2",
+ "p_char_1012_skadi2",
+ "浊心斯卡蒂的信物",
+ "MATERIAL",
+ 600039
+ ],
+ "p_char_003_kalts": [
+ "p_char_003_kalts",
+ "p_char_003_kalts",
+ "凯尔希的信物",
+ "MATERIAL",
+ 600040
+ ],
+ "凯尔希的信物": [
+ "p_char_003_kalts",
+ "p_char_003_kalts",
+ "凯尔希的信物",
+ "MATERIAL",
+ 600040
+ ],
+ "p_char_474_glady": [
+ "p_char_474_glady",
+ "p_char_474_glady",
+ "歌蕾蒂娅的信物",
+ "MATERIAL",
+ 600041
+ ],
+ "歌蕾蒂娅的信物": [
+ "p_char_474_glady",
+ "p_char_474_glady",
+ "歌蕾蒂娅的信物",
+ "MATERIAL",
+ 600041
+ ],
+ "p_char_426_billro": [
+ "p_char_426_billro",
+ "p_char_426_billro",
+ "卡涅利安的信物",
+ "MATERIAL",
+ 600042
+ ],
+ "卡涅利安的信物": [
+ "p_char_426_billro",
+ "p_char_426_billro",
+ "卡涅利安的信物",
+ "MATERIAL",
+ 600042
+ ],
+ "p_char_485_pallas": [
+ "p_char_485_pallas",
+ "p_char_485_pallas",
+ "帕拉斯的信物",
+ "MATERIAL",
+ 600043
+ ],
+ "帕拉斯的信物": [
+ "p_char_485_pallas",
+ "p_char_485_pallas",
+ "帕拉斯的信物",
+ "MATERIAL",
+ 600043
+ ],
+ "p_char_1013_chen2": [
+ "p_char_1013_chen2",
+ "p_char_1013_chen2",
+ "假日威龙陈的信物",
+ "MATERIAL",
+ 600044
+ ],
+ "假日威龙陈的信物": [
+ "p_char_1013_chen2",
+ "p_char_1013_chen2",
+ "假日威龙陈的信物",
+ "MATERIAL",
+ 600044
+ ],
+ "p_char_437_mizuki": [
+ "p_char_437_mizuki",
+ "p_char_437_mizuki",
+ "水月的信物",
+ "MATERIAL",
+ 600045
+ ],
+ "水月的信物": [
+ "p_char_437_mizuki",
+ "p_char_437_mizuki",
+ "水月的信物",
+ "MATERIAL",
+ 600045
+ ],
+ "p_char_430_fartth": [
+ "p_char_430_fartth",
+ "p_char_430_fartth",
+ "远牙的信物",
+ "MATERIAL",
+ 600046
+ ],
+ "远牙的信物": [
+ "p_char_430_fartth",
+ "p_char_430_fartth",
+ "远牙的信物",
+ "MATERIAL",
+ 600046
+ ],
+ "p_char_479_sleach": [
+ "p_char_479_sleach",
+ "p_char_479_sleach",
+ "琴柳的信物",
+ "MATERIAL",
+ 600047
+ ],
+ "琴柳的信物": [
+ "p_char_479_sleach",
+ "p_char_479_sleach",
+ "琴柳的信物",
+ "MATERIAL",
+ 600047
+ ],
+ "p_char_1014_nearl2": [
+ "p_char_1014_nearl2",
+ "p_char_1014_nearl2",
+ "耀骑士临光的信物",
+ "MATERIAL",
+ 600048
+ ],
+ "耀骑士临光的信物": [
+ "p_char_1014_nearl2",
+ "p_char_1014_nearl2",
+ "耀骑士临光的信物",
+ "MATERIAL",
+ 600048
+ ],
+ "p_char_420_flamtl": [
+ "p_char_420_flamtl",
+ "p_char_420_flamtl",
+ "焰尾的信物",
+ "MATERIAL",
+ 600049
+ ],
+ "焰尾的信物": [
+ "p_char_420_flamtl",
+ "p_char_420_flamtl",
+ "焰尾的信物",
+ "MATERIAL",
+ 600049
+ ],
+ "p_char_206_gnosis": [
+ "p_char_206_gnosis",
+ "p_char_206_gnosis",
+ "灵知的信物",
+ "MATERIAL",
+ 600050
+ ],
+ "灵知的信物": [
+ "p_char_206_gnosis",
+ "p_char_206_gnosis",
+ "灵知的信物",
+ "MATERIAL",
+ 600050
+ ],
+ "p_char_322_lmlee": [
+ "p_char_322_lmlee",
+ "p_char_322_lmlee",
+ "老鲤的信物",
+ "MATERIAL",
+ 600051
+ ],
+ "老鲤的信物": [
+ "p_char_322_lmlee",
+ "p_char_322_lmlee",
+ "老鲤的信物",
+ "MATERIAL",
+ 600051
+ ],
+ "p_char_377_gdglow": [
+ "p_char_377_gdglow",
+ "p_char_377_gdglow",
+ "澄闪的信物",
+ "MATERIAL",
+ 600052
+ ],
+ "澄闪的信物": [
+ "p_char_377_gdglow",
+ "p_char_377_gdglow",
+ "澄闪的信物",
+ "MATERIAL",
+ 600052
+ ],
+ "p_char_2023_ling": [
+ "p_char_2023_ling",
+ "p_char_2023_ling",
+ "令的信物",
+ "MATERIAL",
+ 600053
+ ],
+ "令的信物": [
+ "p_char_2023_ling",
+ "p_char_2023_ling",
+ "令的信物",
+ "MATERIAL",
+ 600053
+ ],
+ "p_char_300_phenxi": [
+ "p_char_300_phenxi",
+ "p_char_300_phenxi",
+ "菲亚梅塔的信物",
+ "MATERIAL",
+ 600054
+ ],
+ "菲亚梅塔的信物": [
+ "p_char_300_phenxi",
+ "p_char_300_phenxi",
+ "菲亚梅塔的信物",
+ "MATERIAL",
+ 600054
+ ],
+ "p_char_4039_horn": [
+ "p_char_4039_horn",
+ "p_char_4039_horn",
+ "号角的信物",
+ "MATERIAL",
+ 600055
+ ],
+ "号角的信物": [
+ "p_char_4039_horn",
+ "p_char_4039_horn",
+ "号角的信物",
+ "MATERIAL",
+ 600055
+ ],
+ "p_char_1023_ghost2": [
+ "p_char_1023_ghost2",
+ "p_char_1023_ghost2",
+ "归溟幽灵鲨的信物",
+ "MATERIAL",
+ 600056
+ ],
+ "归溟幽灵鲨的信物": [
+ "p_char_1023_ghost2",
+ "p_char_1023_ghost2",
+ "归溟幽灵鲨的信物",
+ "MATERIAL",
+ 600056
+ ],
+ "p_char_4009_irene": [
+ "p_char_4009_irene",
+ "p_char_4009_irene",
+ "艾丽妮的信物",
+ "MATERIAL",
+ 600057
+ ],
+ "艾丽妮的信物": [
+ "p_char_4009_irene",
+ "p_char_4009_irene",
+ "艾丽妮的信物",
+ "MATERIAL",
+ 600057
+ ],
+ "p_char_4042_lumen": [
+ "p_char_4042_lumen",
+ "p_char_4042_lumen",
+ "流明的信物",
+ "MATERIAL",
+ 600058
+ ],
+ "流明的信物": [
+ "p_char_4042_lumen",
+ "p_char_4042_lumen",
+ "流明的信物",
+ "MATERIAL",
+ 600058
+ ],
+ "p_char_4046_ebnhlz": [
+ "p_char_4046_ebnhlz",
+ "p_char_4046_ebnhlz",
+ "黑键的信物",
+ "MATERIAL",
+ 600059
+ ],
+ "黑键的信物": [
+ "p_char_4046_ebnhlz",
+ "p_char_4046_ebnhlz",
+ "黑键的信物",
+ "MATERIAL",
+ 600059
+ ],
+ "p_char_4048_doroth": [
+ "p_char_4048_doroth",
+ "p_char_4048_doroth",
+ "多萝西的信物",
+ "MATERIAL",
+ 600060
+ ],
+ "多萝西的信物": [
+ "p_char_4048_doroth",
+ "p_char_4048_doroth",
+ "多萝西的信物",
+ "MATERIAL",
+ 600060
+ ],
+ "p_char_1026_gvial2": [
+ "p_char_1026_gvial2",
+ "p_char_1026_gvial2",
+ "百炼嘉维尔的信物",
+ "MATERIAL",
+ 600061
+ ],
+ "百炼嘉维尔的信物": [
+ "p_char_1026_gvial2",
+ "p_char_1026_gvial2",
+ "百炼嘉维尔的信物",
+ "MATERIAL",
+ 600061
+ ],
+ "p_char_4055_bgsnow": [
+ "p_char_4055_bgsnow",
+ "p_char_4055_bgsnow",
+ "鸿雪的信物",
+ "MATERIAL",
+ 600062
+ ],
+ "鸿雪的信物": [
+ "p_char_4055_bgsnow",
+ "p_char_4055_bgsnow",
+ "鸿雪的信物",
+ "MATERIAL",
+ 600062
+ ],
+ "p_char_4064_mlynar": [
+ "p_char_4064_mlynar",
+ "p_char_4064_mlynar",
+ "玛恩纳的信物",
+ "MATERIAL",
+ 600063
+ ],
+ "玛恩纳的信物": [
+ "p_char_4064_mlynar",
+ "p_char_4064_mlynar",
+ "玛恩纳的信物",
+ "MATERIAL",
+ 600063
+ ],
+ "p_char_4072_ironmn": [
+ "p_char_4072_ironmn",
+ "p_char_4072_ironmn",
+ "白铁的信物",
+ "MATERIAL",
+ 600064
+ ],
+ "白铁的信物": [
+ "p_char_4072_ironmn",
+ "p_char_4072_ironmn",
+ "白铁的信物",
+ "MATERIAL",
+ 600064
+ ],
+ "p_char_1028_texas2": [
+ "p_char_1028_texas2",
+ "p_char_1028_texas2",
+ "缄默德克萨斯的信物",
+ "MATERIAL",
+ 600065
+ ],
+ "缄默德克萨斯的信物": [
+ "p_char_1028_texas2",
+ "p_char_1028_texas2",
+ "缄默德克萨斯的信物",
+ "MATERIAL",
+ 600065
+ ],
+ "p_char_4065_judge": [
+ "p_char_4065_judge",
+ "p_char_4065_judge",
+ "斥罪的信物",
+ "MATERIAL",
+ 600066
+ ],
+ "斥罪的信物": [
+ "p_char_4065_judge",
+ "p_char_4065_judge",
+ "斥罪的信物",
+ "MATERIAL",
+ 600066
+ ],
+ "p_char_1020_reed2": [
+ "p_char_1020_reed2",
+ "p_char_1020_reed2",
+ "焰影苇草的信物",
+ "MATERIAL",
+ 600067
+ ],
+ "焰影苇草的信物": [
+ "p_char_1020_reed2",
+ "p_char_1020_reed2",
+ "焰影苇草的信物",
+ "MATERIAL",
+ 600067
+ ],
+ "p_char_4080_lin": [
+ "p_char_4080_lin",
+ "p_char_4080_lin",
+ "林的信物",
+ "MATERIAL",
+ 600068
+ ],
+ "林的信物": [
+ "p_char_4080_lin",
+ "p_char_4080_lin",
+ "林的信物",
+ "MATERIAL",
+ 600068
+ ],
+ "p_char_2024_chyue": [
+ "p_char_2024_chyue",
+ "p_char_2024_chyue",
+ "重岳的信物",
+ "MATERIAL",
+ 600069
+ ],
+ "重岳的信物": [
+ "p_char_2024_chyue",
+ "p_char_2024_chyue",
+ "重岳的信物",
+ "MATERIAL",
+ 600069
+ ],
+ "p_char_4082_qiubai": [
+ "p_char_4082_qiubai",
+ "p_char_4082_qiubai",
+ "仇白的信物",
+ "MATERIAL",
+ 600070
+ ],
+ "仇白的信物": [
+ "p_char_4082_qiubai",
+ "p_char_4082_qiubai",
+ "仇白的信物",
+ "MATERIAL",
+ 600070
+ ],
+ "p_char_1029_yato2": [
+ "p_char_1029_yato2",
+ "p_char_1029_yato2",
+ "麒麟R夜刀的信物",
+ "MATERIAL",
+ 600071
+ ],
+ "麒麟R夜刀的信物": [
+ "p_char_1029_yato2",
+ "p_char_1029_yato2",
+ "麒麟R夜刀的信物",
+ "MATERIAL",
+ 600071
+ ],
+ "p_char_4087_ines": [
+ "p_char_4087_ines",
+ "p_char_4087_ines",
+ "伊内丝的信物",
+ "MATERIAL",
+ 600072
+ ],
+ "伊内丝的信物": [
+ "p_char_4087_ines",
+ "p_char_4087_ines",
+ "伊内丝的信物",
+ "MATERIAL",
+ 600072
+ ],
+ "p_char_249_mlyss": [
+ "p_char_249_mlyss",
+ "p_char_249_mlyss",
+ "缪尔赛思的信物",
+ "MATERIAL",
+ 600073
+ ],
+ "缪尔赛思的信物": [
+ "p_char_249_mlyss",
+ "p_char_249_mlyss",
+ "缪尔赛思的信物",
+ "MATERIAL",
+ 600073
+ ],
+ "p_char_4027_heyak": [
+ "p_char_4027_heyak",
+ "p_char_4027_heyak",
+ "霍尔海雅的信物",
+ "MATERIAL",
+ 600074
+ ],
+ "霍尔海雅的信物": [
+ "p_char_4027_heyak",
+ "p_char_4027_heyak",
+ "霍尔海雅的信物",
+ "MATERIAL",
+ 600074
+ ],
+ "p_char_1031_slent2": [
+ "p_char_1031_slent2",
+ "p_char_1031_slent2",
+ "淬羽赫默的信物",
+ "MATERIAL",
+ 600075
+ ],
+ "淬羽赫默的信物": [
+ "p_char_1031_slent2",
+ "p_char_1031_slent2",
+ "淬羽赫默的信物",
+ "MATERIAL",
+ 600075
+ ],
+ "p_char_1032_excu2": [
+ "p_char_1032_excu2",
+ "p_char_1032_excu2",
+ "圣约送葬人的信物",
+ "MATERIAL",
+ 600076
+ ],
+ "圣约送葬人的信物": [
+ "p_char_1032_excu2",
+ "p_char_1032_excu2",
+ "圣约送葬人的信物",
+ "MATERIAL",
+ 600076
+ ],
+ "p_char_2012_typhon": [
+ "p_char_2012_typhon",
+ "p_char_2012_typhon",
+ "提丰的信物",
+ "MATERIAL",
+ 600077
+ ],
+ "提丰的信物": [
+ "p_char_2012_typhon",
+ "p_char_2012_typhon",
+ "提丰的信物",
+ "MATERIAL",
+ 600077
+ ],
+ "p_char_1033_swire2": [
+ "p_char_1033_swire2",
+ "p_char_1033_swire2",
+ "琳琅诗怀雅的信物",
+ "MATERIAL",
+ 600078
+ ],
+ "琳琅诗怀雅的信物": [
+ "p_char_1033_swire2",
+ "p_char_1033_swire2",
+ "琳琅诗怀雅的信物",
+ "MATERIAL",
+ 600078
+ ],
+ "p_char_1016_agoat2": [
+ "p_char_1016_agoat2",
+ "p_char_1016_agoat2",
+ "纯烬艾雅法拉的信物",
+ "MATERIAL",
+ 600079
+ ],
+ "纯烬艾雅法拉的信物": [
+ "p_char_1016_agoat2",
+ "p_char_1016_agoat2",
+ "纯烬艾雅法拉的信物",
+ "MATERIAL",
+ 600079
+ ],
+ "p_char_1034_jesca2": [
+ "p_char_1034_jesca2",
+ "p_char_1034_jesca2",
+ "涤火杰西卡的信物",
+ "MATERIAL",
+ 600080
+ ],
+ "涤火杰西卡的信物": [
+ "p_char_1034_jesca2",
+ "p_char_1034_jesca2",
+ "涤火杰西卡的信物",
+ "MATERIAL",
+ 600080
+ ],
+ "p_char_4088_hodrer": [
+ "p_char_4088_hodrer",
+ "p_char_4088_hodrer",
+ "赫德雷的信物",
+ "MATERIAL",
+ 600081
+ ],
+ "赫德雷的信物": [
+ "p_char_4088_hodrer",
+ "p_char_4088_hodrer",
+ "赫德雷的信物",
+ "MATERIAL",
+ 600081
+ ],
+ "p_char_4098_vvana": [
+ "p_char_4098_vvana",
+ "p_char_4098_vvana",
+ "薇薇安娜的信物",
+ "MATERIAL",
+ 600082
+ ],
+ "薇薇安娜的信物": [
+ "p_char_4098_vvana",
+ "p_char_4098_vvana",
+ "薇薇安娜的信物",
+ "MATERIAL",
+ 600082
+ ],
+ "p_char_245_cello": [
+ "p_char_245_cello",
+ "p_char_245_cello",
+ "塑心的信物",
+ "MATERIAL",
+ 600083
+ ],
+ "塑心的信物": [
+ "p_char_245_cello",
+ "p_char_245_cello",
+ "塑心的信物",
+ "MATERIAL",
+ 600083
+ ],
+ "p_char_4116_blkkgt": [
+ "p_char_4116_blkkgt",
+ "p_char_4116_blkkgt",
+ "锏的信物",
+ "MATERIAL",
+ 600084
+ ],
+ "锏的信物": [
+ "p_char_4116_blkkgt",
+ "p_char_4116_blkkgt",
+ "锏的信物",
+ "MATERIAL",
+ 600084
+ ],
+ "p_char_4117_ray": [
+ "p_char_4117_ray",
+ "p_char_4117_ray",
+ "莱伊的信物",
+ "MATERIAL",
+ 600085
+ ],
+ "莱伊的信物": [
+ "p_char_4117_ray",
+ "p_char_4117_ray",
+ "莱伊的信物",
+ "MATERIAL",
+ 600085
+ ],
+ "p_char_2025_shu": [
+ "p_char_2025_shu",
+ "p_char_2025_shu",
+ "黍的信物",
+ "MATERIAL",
+ 600086
+ ],
+ "黍的信物": [
+ "p_char_2025_shu",
+ "p_char_2025_shu",
+ "黍的信物",
+ "MATERIAL",
+ 600086
+ ],
+ "p_char_4121_zuole": [
+ "p_char_4121_zuole",
+ "p_char_4121_zuole",
+ "左乐的信物",
+ "MATERIAL",
+ 600087
+ ],
+ "左乐的信物": [
+ "p_char_4121_zuole",
+ "p_char_4121_zuole",
+ "左乐的信物",
+ "MATERIAL",
+ 600087
+ ],
+ "p_char_4123_ela": [
+ "p_char_4123_ela",
+ "p_char_4123_ela",
+ "艾拉的信物",
+ "MATERIAL",
+ 600088
+ ],
+ "艾拉的信物": [
+ "p_char_4123_ela",
+ "p_char_4123_ela",
+ "艾拉的信物",
+ "MATERIAL",
+ 600088
+ ],
+ "p_char_4132_ascln": [
+ "p_char_4132_ascln",
+ "p_char_4132_ascln",
+ "阿斯卡纶的信物",
+ "MATERIAL",
+ 600089
+ ],
+ "阿斯卡纶的信物": [
+ "p_char_4132_ascln",
+ "p_char_4132_ascln",
+ "阿斯卡纶的信物",
+ "MATERIAL",
+ 600089
+ ],
+ "p_char_1035_wisdel": [
+ "p_char_1035_wisdel",
+ "p_char_1035_wisdel",
+ "维什戴尔的信物",
+ "MATERIAL",
+ 600090
+ ],
+ "维什戴尔的信物": [
+ "p_char_1035_wisdel",
+ "p_char_1035_wisdel",
+ "维什戴尔的信物",
+ "MATERIAL",
+ 600090
+ ],
+ "p_char_4133_logos": [
+ "p_char_4133_logos",
+ "p_char_4133_logos",
+ "逻各斯的信物",
+ "MATERIAL",
+ 600091
+ ],
+ "逻各斯的信物": [
+ "p_char_4133_logos",
+ "p_char_4133_logos",
+ "逻各斯的信物",
+ "MATERIAL",
+ 600091
+ ],
+ "p_char_4134_cetsyr": [
+ "p_char_4134_cetsyr",
+ "p_char_4134_cetsyr",
+ "魔王的信物",
+ "MATERIAL",
+ 600092
+ ],
+ "魔王的信物": [
+ "p_char_4134_cetsyr",
+ "p_char_4134_cetsyr",
+ "魔王的信物",
+ "MATERIAL",
+ 600092
+ ],
+ "p_char_4145_ulpia": [
+ "p_char_4145_ulpia",
+ "p_char_4145_ulpia",
+ "乌尔比安的信物",
+ "MATERIAL",
+ 600093
+ ],
+ "乌尔比安的信物": [
+ "p_char_4145_ulpia",
+ "p_char_4145_ulpia",
+ "乌尔比安的信物",
+ "MATERIAL",
+ 600093
+ ],
+ "p_char_4146_nymph": [
+ "p_char_4146_nymph",
+ "p_char_4146_nymph",
+ "妮芙的信物",
+ "MATERIAL",
+ 600094
+ ],
+ "妮芙的信物": [
+ "p_char_4146_nymph",
+ "p_char_4146_nymph",
+ "妮芙的信物",
+ "MATERIAL",
+ 600094
+ ],
+ "p_char_4138_narant": [
+ "p_char_4138_narant",
+ "p_char_4138_narant",
+ "娜仁图亚的信物",
+ "MATERIAL",
+ 600095
+ ],
+ "娜仁图亚的信物": [
+ "p_char_4138_narant",
+ "p_char_4138_narant",
+ "娜仁图亚的信物",
+ "MATERIAL",
+ 600095
+ ],
+ "p_char_4058_pepe": [
+ "p_char_4058_pepe",
+ "p_char_4058_pepe",
+ "佩佩的信物",
+ "MATERIAL",
+ 600096
+ ],
+ "佩佩的信物": [
+ "p_char_4058_pepe",
+ "p_char_4058_pepe",
+ "佩佩的信物",
+ "MATERIAL",
+ 600096
+ ],
+ "p_char_4141_marcil": [
+ "p_char_4141_marcil",
+ "p_char_4141_marcil",
+ "玛露西尔的信物",
+ "MATERIAL",
+ 600097
+ ],
+ "玛露西尔的信物": [
+ "p_char_4141_marcil",
+ "p_char_4141_marcil",
+ "玛露西尔的信物",
+ "MATERIAL",
+ 600097
+ ],
+ "class_p_char_123_fang": [
+ "class_p_char_123_fang",
+ "class_p_char_123_fang",
+ "芬的中坚信物",
+ "MATERIAL",
+ 1500001
+ ],
+ "芬的中坚信物": [
+ "class_p_char_123_fang",
+ "class_p_char_123_fang",
+ "芬的中坚信物",
+ "MATERIAL",
+ 1500001
+ ],
+ "class_p_char_240_wyvern": [
+ "class_p_char_240_wyvern",
+ "class_p_char_240_wyvern",
+ "香草的中坚信物",
+ "MATERIAL",
+ 1500002
+ ],
+ "香草的中坚信物": [
+ "class_p_char_240_wyvern",
+ "class_p_char_240_wyvern",
+ "香草的中坚信物",
+ "MATERIAL",
+ 1500002
+ ],
+ "class_p_char_192_falco": [
+ "class_p_char_192_falco",
+ "class_p_char_192_falco",
+ "翎羽的中坚信物",
+ "MATERIAL",
+ 1500003
+ ],
+ "翎羽的中坚信物": [
+ "class_p_char_192_falco",
+ "class_p_char_192_falco",
+ "翎羽的中坚信物",
+ "MATERIAL",
+ 1500003
+ ],
+ "class_p_char_208_melan": [
+ "class_p_char_208_melan",
+ "class_p_char_208_melan",
+ "玫兰莎的中坚信物",
+ "MATERIAL",
+ 1500004
+ ],
+ "玫兰莎的中坚信物": [
+ "class_p_char_208_melan",
+ "class_p_char_208_melan",
+ "玫兰莎的中坚信物",
+ "MATERIAL",
+ 1500004
+ ],
+ "class_p_char_209_ardign": [
+ "class_p_char_209_ardign",
+ "class_p_char_209_ardign",
+ "卡缇的中坚信物",
+ "MATERIAL",
+ 1500005
+ ],
+ "卡缇的中坚信物": [
+ "class_p_char_209_ardign",
+ "class_p_char_209_ardign",
+ "卡缇的中坚信物",
+ "MATERIAL",
+ 1500005
+ ],
+ "class_p_char_122_beagle": [
+ "class_p_char_122_beagle",
+ "class_p_char_122_beagle",
+ "米格鲁的中坚信物",
+ "MATERIAL",
+ 1500006
+ ],
+ "米格鲁的中坚信物": [
+ "class_p_char_122_beagle",
+ "class_p_char_122_beagle",
+ "米格鲁的中坚信物",
+ "MATERIAL",
+ 1500006
+ ],
+ "class_p_char_124_kroos": [
+ "class_p_char_124_kroos",
+ "class_p_char_124_kroos",
+ "克洛丝的中坚信物",
+ "MATERIAL",
+ 1500007
+ ],
+ "克洛丝的中坚信物": [
+ "class_p_char_124_kroos",
+ "class_p_char_124_kroos",
+ "克洛丝的中坚信物",
+ "MATERIAL",
+ 1500007
+ ],
+ "class_p_char_121_lava": [
+ "class_p_char_121_lava",
+ "class_p_char_121_lava",
+ "炎熔的中坚信物",
+ "MATERIAL",
+ 1500009
+ ],
+ "炎熔的中坚信物": [
+ "class_p_char_121_lava",
+ "class_p_char_121_lava",
+ "炎熔的中坚信物",
+ "MATERIAL",
+ 1500009
+ ],
+ "class_p_char_120_hibisc": [
+ "class_p_char_120_hibisc",
+ "class_p_char_120_hibisc",
+ "芙蓉的中坚信物",
+ "MATERIAL",
+ 1500010
+ ],
+ "芙蓉的中坚信物": [
+ "class_p_char_120_hibisc",
+ "class_p_char_120_hibisc",
+ "芙蓉的中坚信物",
+ "MATERIAL",
+ 1500010
+ ],
+ "class_p_char_212_ansel": [
+ "class_p_char_212_ansel",
+ "class_p_char_212_ansel",
+ "安赛尔的中坚信物",
+ "MATERIAL",
+ 1500011
+ ],
+ "安赛尔的中坚信物": [
+ "class_p_char_212_ansel",
+ "class_p_char_212_ansel",
+ "安赛尔的中坚信物",
+ "MATERIAL",
+ 1500011
+ ],
+ "class_p_char_210_stward": [
+ "class_p_char_210_stward",
+ "class_p_char_210_stward",
+ "史都华德的中坚信物",
+ "MATERIAL",
+ 1500012
+ ],
+ "史都华德的中坚信物": [
+ "class_p_char_210_stward",
+ "class_p_char_210_stward",
+ "史都华德的中坚信物",
+ "MATERIAL",
+ 1500012
+ ],
+ "class_p_char_278_orchid": [
+ "class_p_char_278_orchid",
+ "class_p_char_278_orchid",
+ "梓兰的中坚信物",
+ "MATERIAL",
+ 1500013
+ ],
+ "梓兰的中坚信物": [
+ "class_p_char_278_orchid",
+ "class_p_char_278_orchid",
+ "梓兰的中坚信物",
+ "MATERIAL",
+ 1500013
+ ],
+ "class_p_char_282_catap": [
+ "class_p_char_282_catap",
+ "class_p_char_282_catap",
+ "空爆的中坚信物",
+ "MATERIAL",
+ 1500014
+ ],
+ "空爆的中坚信物": [
+ "class_p_char_282_catap",
+ "class_p_char_282_catap",
+ "空爆的中坚信物",
+ "MATERIAL",
+ 1500014
+ ],
+ "class_p_char_283_midn": [
+ "class_p_char_283_midn",
+ "class_p_char_283_midn",
+ "月见夜的中坚信物",
+ "MATERIAL",
+ 1500015
+ ],
+ "月见夜的中坚信物": [
+ "class_p_char_283_midn",
+ "class_p_char_283_midn",
+ "月见夜的中坚信物",
+ "MATERIAL",
+ 1500015
+ ],
+ "class_p_char_284_spot": [
+ "class_p_char_284_spot",
+ "class_p_char_284_spot",
+ "斑点的中坚信物",
+ "MATERIAL",
+ 1500016
+ ],
+ "斑点的中坚信物": [
+ "class_p_char_284_spot",
+ "class_p_char_284_spot",
+ "斑点的中坚信物",
+ "MATERIAL",
+ 1500016
+ ],
+ "class_p_char_281_popka": [
+ "class_p_char_281_popka",
+ "class_p_char_281_popka",
+ "泡普卡的中坚信物",
+ "MATERIAL",
+ 1500017
+ ],
+ "泡普卡的中坚信物": [
+ "class_p_char_281_popka",
+ "class_p_char_281_popka",
+ "泡普卡的中坚信物",
+ "MATERIAL",
+ 1500017
+ ],
+ "class_p_char_141_nights": [
+ "class_p_char_141_nights",
+ "class_p_char_141_nights",
+ "夜烟的中坚信物",
+ "MATERIAL",
+ 1400001
+ ],
+ "夜烟的中坚信物": [
+ "class_p_char_141_nights",
+ "class_p_char_141_nights",
+ "夜烟的中坚信物",
+ "MATERIAL",
+ 1400001
+ ],
+ "class_p_char_109_fmout": [
+ "class_p_char_109_fmout",
+ "class_p_char_109_fmout",
+ "远山的中坚信物",
+ "MATERIAL",
+ 1400002
+ ],
+ "远山的中坚信物": [
+ "class_p_char_109_fmout",
+ "class_p_char_109_fmout",
+ "远山的中坚信物",
+ "MATERIAL",
+ 1400002
+ ],
+ "class_p_char_235_jesica": [
+ "class_p_char_235_jesica",
+ "class_p_char_235_jesica",
+ "杰西卡的中坚信物",
+ "MATERIAL",
+ 1400003
+ ],
+ "杰西卡的中坚信物": [
+ "class_p_char_235_jesica",
+ "class_p_char_235_jesica",
+ "杰西卡的中坚信物",
+ "MATERIAL",
+ 1400003
+ ],
+ "class_p_char_126_shotst": [
+ "class_p_char_126_shotst",
+ "class_p_char_126_shotst",
+ "流星的中坚信物",
+ "MATERIAL",
+ 1400004
+ ],
+ "流星的中坚信物": [
+ "class_p_char_126_shotst",
+ "class_p_char_126_shotst",
+ "流星的中坚信物",
+ "MATERIAL",
+ 1400004
+ ],
+ "class_p_char_118_yuki": [
+ "class_p_char_118_yuki",
+ "class_p_char_118_yuki",
+ "白雪的中坚信物",
+ "MATERIAL",
+ 1400005
+ ],
+ "白雪的中坚信物": [
+ "class_p_char_118_yuki",
+ "class_p_char_118_yuki",
+ "白雪的中坚信物",
+ "MATERIAL",
+ 1400005
+ ],
+ "class_p_char_149_scave": [
+ "class_p_char_149_scave",
+ "class_p_char_149_scave",
+ "清道夫的中坚信物",
+ "MATERIAL",
+ 1400007
+ ],
+ "清道夫的中坚信物": [
+ "class_p_char_149_scave",
+ "class_p_char_149_scave",
+ "清道夫的中坚信物",
+ "MATERIAL",
+ 1400007
+ ],
+ "class_p_char_290_vigna": [
+ "class_p_char_290_vigna",
+ "class_p_char_290_vigna",
+ "红豆的中坚信物",
+ "MATERIAL",
+ 1400008
+ ],
+ "红豆的中坚信物": [
+ "class_p_char_290_vigna",
+ "class_p_char_290_vigna",
+ "红豆的中坚信物",
+ "MATERIAL",
+ 1400008
+ ],
+ "class_p_char_130_doberm": [
+ "class_p_char_130_doberm",
+ "class_p_char_130_doberm",
+ "杜宾的中坚信物",
+ "MATERIAL",
+ 1400009
+ ],
+ "杜宾的中坚信物": [
+ "class_p_char_130_doberm",
+ "class_p_char_130_doberm",
+ "杜宾的中坚信物",
+ "MATERIAL",
+ 1400009
+ ],
+ "class_p_char_289_gyuki": [
+ "class_p_char_289_gyuki",
+ "class_p_char_289_gyuki",
+ "缠丸的中坚信物",
+ "MATERIAL",
+ 1400010
+ ],
+ "缠丸的中坚信物": [
+ "class_p_char_289_gyuki",
+ "class_p_char_289_gyuki",
+ "缠丸的中坚信物",
+ "MATERIAL",
+ 1400010
+ ],
+ "class_p_char_193_frostl": [
+ "class_p_char_193_frostl",
+ "class_p_char_193_frostl",
+ "霜叶的中坚信物",
+ "MATERIAL",
+ 1400011
+ ],
+ "霜叶的中坚信物": [
+ "class_p_char_193_frostl",
+ "class_p_char_193_frostl",
+ "霜叶的中坚信物",
+ "MATERIAL",
+ 1400011
+ ],
+ "class_p_char_185_frncat": [
+ "class_p_char_185_frncat",
+ "class_p_char_185_frncat",
+ "慕斯的中坚信物",
+ "MATERIAL",
+ 1400013
+ ],
+ "慕斯的中坚信物": [
+ "class_p_char_185_frncat",
+ "class_p_char_185_frncat",
+ "慕斯的中坚信物",
+ "MATERIAL",
+ 1400013
+ ],
+ "class_p_char_237_gravel": [
+ "class_p_char_237_gravel",
+ "class_p_char_237_gravel",
+ "砾的中坚信物",
+ "MATERIAL",
+ 1400014
+ ],
+ "砾的中坚信物": [
+ "class_p_char_237_gravel",
+ "class_p_char_237_gravel",
+ "砾的中坚信物",
+ "MATERIAL",
+ 1400014
+ ],
+ "class_p_char_236_rope": [
+ "class_p_char_236_rope",
+ "class_p_char_236_rope",
+ "暗索的中坚信物",
+ "MATERIAL",
+ 1400015
+ ],
+ "暗索的中坚信物": [
+ "class_p_char_236_rope",
+ "class_p_char_236_rope",
+ "暗索的中坚信物",
+ "MATERIAL",
+ 1400015
+ ],
+ "class_p_char_117_myrrh": [
+ "class_p_char_117_myrrh",
+ "class_p_char_117_myrrh",
+ "末药的中坚信物",
+ "MATERIAL",
+ 1400016
+ ],
+ "末药的中坚信物": [
+ "class_p_char_117_myrrh",
+ "class_p_char_117_myrrh",
+ "末药的中坚信物",
+ "MATERIAL",
+ 1400016
+ ],
+ "class_p_char_181_flower": [
+ "class_p_char_181_flower",
+ "class_p_char_181_flower",
+ "调香师的中坚信物",
+ "MATERIAL",
+ 1400018
+ ],
+ "调香师的中坚信物": [
+ "class_p_char_181_flower",
+ "class_p_char_181_flower",
+ "调香师的中坚信物",
+ "MATERIAL",
+ 1400018
+ ],
+ "class_p_char_199_yak": [
+ "class_p_char_199_yak",
+ "class_p_char_199_yak",
+ "角峰的中坚信物",
+ "MATERIAL",
+ 1400019
+ ],
+ "角峰的中坚信物": [
+ "class_p_char_199_yak",
+ "class_p_char_199_yak",
+ "角峰的中坚信物",
+ "MATERIAL",
+ 1400019
+ ],
+ "class_p_char_150_snakek": [
+ "class_p_char_150_snakek",
+ "class_p_char_150_snakek",
+ "蛇屠箱的中坚信物",
+ "MATERIAL",
+ 1400020
+ ],
+ "蛇屠箱的中坚信物": [
+ "class_p_char_150_snakek",
+ "class_p_char_150_snakek",
+ "蛇屠箱的中坚信物",
+ "MATERIAL",
+ 1400020
+ ],
+ "class_p_char_196_sunbr": [
+ "class_p_char_196_sunbr",
+ "class_p_char_196_sunbr",
+ "古米的中坚信物",
+ "MATERIAL",
+ 1400021
+ ],
+ "古米的中坚信物": [
+ "class_p_char_196_sunbr",
+ "class_p_char_196_sunbr",
+ "古米的中坚信物",
+ "MATERIAL",
+ 1400021
+ ],
+ "class_p_char_110_deepcl": [
+ "class_p_char_110_deepcl",
+ "class_p_char_110_deepcl",
+ "深海色的中坚信物",
+ "MATERIAL",
+ 1400022
+ ],
+ "深海色的中坚信物": [
+ "class_p_char_110_deepcl",
+ "class_p_char_110_deepcl",
+ "深海色的中坚信物",
+ "MATERIAL",
+ 1400022
+ ],
+ "class_p_char_183_skgoat": [
+ "class_p_char_183_skgoat",
+ "class_p_char_183_skgoat",
+ "地灵的中坚信物",
+ "MATERIAL",
+ 1400023
+ ],
+ "地灵的中坚信物": [
+ "class_p_char_183_skgoat",
+ "class_p_char_183_skgoat",
+ "地灵的中坚信物",
+ "MATERIAL",
+ 1400023
+ ],
+ "class_p_char_277_sqrrel": [
+ "class_p_char_277_sqrrel",
+ "class_p_char_277_sqrrel",
+ "阿消的中坚信物",
+ "MATERIAL",
+ 1400024
+ ],
+ "阿消的中坚信物": [
+ "class_p_char_277_sqrrel",
+ "class_p_char_277_sqrrel",
+ "阿消的中坚信物",
+ "MATERIAL",
+ 1400024
+ ],
+ "class_p_char_137_brownb": [
+ "class_p_char_137_brownb",
+ "class_p_char_137_brownb",
+ "猎蜂的中坚信物",
+ "MATERIAL",
+ 1400025
+ ],
+ "猎蜂的中坚信物": [
+ "class_p_char_137_brownb",
+ "class_p_char_137_brownb",
+ "猎蜂的中坚信物",
+ "MATERIAL",
+ 1400025
+ ],
+ "class_p_char_253_greyy": [
+ "class_p_char_253_greyy",
+ "class_p_char_253_greyy",
+ "格雷伊的中坚信物",
+ "MATERIAL",
+ 1400026
+ ],
+ "格雷伊的中坚信物": [
+ "class_p_char_253_greyy",
+ "class_p_char_253_greyy",
+ "格雷伊的中坚信物",
+ "MATERIAL",
+ 1400026
+ ],
+ "class_p_char_298_susuro": [
+ "class_p_char_298_susuro",
+ "class_p_char_298_susuro",
+ "苏苏洛的中坚信物",
+ "MATERIAL",
+ 1400028
+ ],
+ "苏苏洛的中坚信物": [
+ "class_p_char_298_susuro",
+ "class_p_char_298_susuro",
+ "苏苏洛的中坚信物",
+ "MATERIAL",
+ 1400028
+ ],
+ "class_p_char_151_myrtle": [
+ "class_p_char_151_myrtle",
+ "class_p_char_151_myrtle",
+ "桃金娘的中坚信物",
+ "MATERIAL",
+ 1400027
+ ],
+ "桃金娘的中坚信物": [
+ "class_p_char_151_myrtle",
+ "class_p_char_151_myrtle",
+ "桃金娘的中坚信物",
+ "MATERIAL",
+ 1400027
+ ],
+ "class_p_char_190_clour": [
+ "class_p_char_190_clour",
+ "class_p_char_190_clour",
+ "红云的中坚信物",
+ "MATERIAL",
+ 1400031
+ ],
+ "红云的中坚信物": [
+ "class_p_char_190_clour",
+ "class_p_char_190_clour",
+ "红云的中坚信物",
+ "MATERIAL",
+ 1400031
+ ],
+ "class_p_char_133_mm": [
+ "class_p_char_133_mm",
+ "class_p_char_133_mm",
+ "梅的中坚信物",
+ "MATERIAL",
+ 1400032
+ ],
+ "梅的中坚信物": [
+ "class_p_char_133_mm",
+ "class_p_char_133_mm",
+ "梅的中坚信物",
+ "MATERIAL",
+ 1400032
+ ],
+ "class_p_char_302_glaze": [
+ "class_p_char_302_glaze",
+ "class_p_char_302_glaze",
+ "安比尔的中坚信物",
+ "MATERIAL",
+ 1400033
+ ],
+ "安比尔的中坚信物": [
+ "class_p_char_302_glaze",
+ "class_p_char_302_glaze",
+ "安比尔的中坚信物",
+ "MATERIAL",
+ 1400033
+ ],
+ "class_p_char_337_utage": [
+ "class_p_char_337_utage",
+ "class_p_char_337_utage",
+ "宴的中坚信物",
+ "MATERIAL",
+ 1400035
+ ],
+ "宴的中坚信物": [
+ "class_p_char_337_utage",
+ "class_p_char_337_utage",
+ "宴的中坚信物",
+ "MATERIAL",
+ 1400035
+ ],
+ "class_p_char_301_cutter": [
+ "class_p_char_301_cutter",
+ "class_p_char_301_cutter",
+ "刻刀的中坚信物",
+ "MATERIAL",
+ 1400036
+ ],
+ "刻刀的中坚信物": [
+ "class_p_char_301_cutter",
+ "class_p_char_301_cutter",
+ "刻刀的中坚信物",
+ "MATERIAL",
+ 1400036
+ ],
+ "class_p_char_258_podego": [
+ "class_p_char_258_podego",
+ "class_p_char_258_podego",
+ "波登可的中坚信物",
+ "MATERIAL",
+ 1400037
+ ],
+ "波登可的中坚信物": [
+ "class_p_char_258_podego",
+ "class_p_char_258_podego",
+ "波登可的中坚信物",
+ "MATERIAL",
+ 1400037
+ ],
+ "class_p_char_328_cammou": [
+ "class_p_char_328_cammou",
+ "class_p_char_328_cammou",
+ "卡达的中坚信物",
+ "MATERIAL",
+ 1400038
+ ],
+ "卡达的中坚信物": [
+ "class_p_char_328_cammou",
+ "class_p_char_328_cammou",
+ "卡达的中坚信物",
+ "MATERIAL",
+ 1400038
+ ],
+ "class_p_char_272_strong": [
+ "class_p_char_272_strong",
+ "class_p_char_272_strong",
+ "孑的中坚信物",
+ "MATERIAL",
+ 1400039
+ ],
+ "孑的中坚信物": [
+ "class_p_char_272_strong",
+ "class_p_char_272_strong",
+ "孑的中坚信物",
+ "MATERIAL",
+ 1400039
+ ],
+ "class_p_char_366_acdrop": [
+ "class_p_char_366_acdrop",
+ "class_p_char_366_acdrop",
+ "酸糖的中坚信物",
+ "MATERIAL",
+ 1400040
+ ],
+ "酸糖的中坚信物": [
+ "class_p_char_366_acdrop",
+ "class_p_char_366_acdrop",
+ "酸糖的中坚信物",
+ "MATERIAL",
+ 1400040
+ ],
+ "class_p_char_271_spikes": [
+ "class_p_char_271_spikes",
+ "class_p_char_271_spikes",
+ "芳汀的中坚信物",
+ "MATERIAL",
+ 1400041
+ ],
+ "芳汀的中坚信物": [
+ "class_p_char_271_spikes",
+ "class_p_char_271_spikes",
+ "芳汀的中坚信物",
+ "MATERIAL",
+ 1400041
+ ],
+ "class_p_char_381_bubble": [
+ "class_p_char_381_bubble",
+ "class_p_char_381_bubble",
+ "泡泡的中坚信物",
+ "MATERIAL",
+ 1400042
+ ],
+ "泡泡的中坚信物": [
+ "class_p_char_381_bubble",
+ "class_p_char_381_bubble",
+ "泡泡的中坚信物",
+ "MATERIAL",
+ 1400042
+ ],
+ "class_p_char_347_jaksel": [
+ "class_p_char_347_jaksel",
+ "class_p_char_347_jaksel",
+ "杰克的中坚信物",
+ "MATERIAL",
+ 1400043
+ ],
+ "杰克的中坚信物": [
+ "class_p_char_347_jaksel",
+ "class_p_char_347_jaksel",
+ "杰克的中坚信物",
+ "MATERIAL",
+ 1400043
+ ],
+ "class_p_char_440_pinecn": [
+ "class_p_char_440_pinecn",
+ "class_p_char_440_pinecn",
+ "松果的中坚信物",
+ "MATERIAL",
+ 1400044
+ ],
+ "松果的中坚信物": [
+ "class_p_char_440_pinecn",
+ "class_p_char_440_pinecn",
+ "松果的中坚信物",
+ "MATERIAL",
+ 1400044
+ ],
+ "class_p_char_452_bstalk": [
+ "class_p_char_452_bstalk",
+ "class_p_char_452_bstalk",
+ "豆苗的中坚信物",
+ "MATERIAL",
+ 1400045
+ ],
+ "豆苗的中坚信物": [
+ "class_p_char_452_bstalk",
+ "class_p_char_452_bstalk",
+ "豆苗的中坚信物",
+ "MATERIAL",
+ 1400045
+ ],
+ "class_p_char_469_indigo": [
+ "class_p_char_469_indigo",
+ "class_p_char_469_indigo",
+ "深靛的中坚信物",
+ "MATERIAL",
+ 1400046
+ ],
+ "深靛的中坚信物": [
+ "class_p_char_469_indigo",
+ "class_p_char_469_indigo",
+ "深靛的中坚信物",
+ "MATERIAL",
+ 1400046
+ ],
+ "class_p_char_484_robrta": [
+ "class_p_char_484_robrta",
+ "class_p_char_484_robrta",
+ "罗比菈塔的中坚信物",
+ "MATERIAL",
+ 1400047
+ ],
+ "罗比菈塔的中坚信物": [
+ "class_p_char_484_robrta",
+ "class_p_char_484_robrta",
+ "罗比菈塔的中坚信物",
+ "MATERIAL",
+ 1400047
+ ],
+ "class_p_char_4041_chnut": [
+ "class_p_char_4041_chnut",
+ "class_p_char_4041_chnut",
+ "褐果的中坚信物",
+ "MATERIAL",
+ 1400049
+ ],
+ "褐果的中坚信物": [
+ "class_p_char_4041_chnut",
+ "class_p_char_4041_chnut",
+ "褐果的中坚信物",
+ "MATERIAL",
+ 1400049
+ ],
+ "class_p_char_4062_totter": [
+ "class_p_char_4062_totter",
+ "class_p_char_4062_totter",
+ "铅踝的中坚信物",
+ "MATERIAL",
+ 1400050
+ ],
+ "铅踝的中坚信物": [
+ "class_p_char_4062_totter",
+ "class_p_char_4062_totter",
+ "铅踝的中坚信物",
+ "MATERIAL",
+ 1400050
+ ],
+ "class_p_char_491_humus": [
+ "class_p_char_491_humus",
+ "class_p_char_491_humus",
+ "休谟斯的中坚信物",
+ "MATERIAL",
+ 1400052
+ ],
+ "休谟斯的中坚信物": [
+ "class_p_char_491_humus",
+ "class_p_char_491_humus",
+ "休谟斯的中坚信物",
+ "MATERIAL",
+ 1400052
+ ],
+ "class_p_char_4107_vrdant": [
+ "class_p_char_4107_vrdant",
+ "class_p_char_4107_vrdant",
+ "维荻的中坚信物",
+ "MATERIAL",
+ 1400053
+ ],
+ "维荻的中坚信物": [
+ "class_p_char_4107_vrdant",
+ "class_p_char_4107_vrdant",
+ "维荻的中坚信物",
+ "MATERIAL",
+ 1400053
+ ],
+ "class_p_char_128_plosis": [
+ "class_p_char_128_plosis",
+ "class_p_char_128_plosis",
+ "白面鸮的中坚信物",
+ "MATERIAL",
+ 1300001
+ ],
+ "白面鸮的中坚信物": [
+ "class_p_char_128_plosis",
+ "class_p_char_128_plosis",
+ "白面鸮的中坚信物",
+ "MATERIAL",
+ 1300001
+ ],
+ "class_p_char_115_headbr": [
+ "class_p_char_115_headbr",
+ "class_p_char_115_headbr",
+ "凛冬的中坚信物",
+ "MATERIAL",
+ 1300002
+ ],
+ "凛冬的中坚信物": [
+ "class_p_char_115_headbr",
+ "class_p_char_115_headbr",
+ "凛冬的中坚信物",
+ "MATERIAL",
+ 1300002
+ ],
+ "class_p_char_102_texas": [
+ "class_p_char_102_texas",
+ "class_p_char_102_texas",
+ "德克萨斯的中坚信物",
+ "MATERIAL",
+ 1300003
+ ],
+ "德克萨斯的中坚信物": [
+ "class_p_char_102_texas",
+ "class_p_char_102_texas",
+ "德克萨斯的中坚信物",
+ "MATERIAL",
+ 1300003
+ ],
+ "class_p_char_106_franka": [
+ "class_p_char_106_franka",
+ "class_p_char_106_franka",
+ "芙兰卡的中坚信物",
+ "MATERIAL",
+ 1300004
+ ],
+ "芙兰卡的中坚信物": [
+ "class_p_char_106_franka",
+ "class_p_char_106_franka",
+ "芙兰卡的中坚信物",
+ "MATERIAL",
+ 1300004
+ ],
+ "class_p_char_140_whitew": [
+ "class_p_char_140_whitew",
+ "class_p_char_140_whitew",
+ "拉普兰德的中坚信物",
+ "MATERIAL",
+ 1300006
+ ],
+ "拉普兰德的中坚信物": [
+ "class_p_char_140_whitew",
+ "class_p_char_140_whitew",
+ "拉普兰德的中坚信物",
+ "MATERIAL",
+ 1300006
+ ],
+ "class_p_char_143_ghost": [
+ "class_p_char_143_ghost",
+ "class_p_char_143_ghost",
+ "幽灵鲨的中坚信物",
+ "MATERIAL",
+ 1300007
+ ],
+ "幽灵鲨的中坚信物": [
+ "class_p_char_143_ghost",
+ "class_p_char_143_ghost",
+ "幽灵鲨的中坚信物",
+ "MATERIAL",
+ 1300007
+ ],
+ "class_p_char_129_bluep": [
+ "class_p_char_129_bluep",
+ "class_p_char_129_bluep",
+ "蓝毒的中坚信物",
+ "MATERIAL",
+ 1300008
+ ],
+ "蓝毒的中坚信物": [
+ "class_p_char_129_bluep",
+ "class_p_char_129_bluep",
+ "蓝毒的中坚信物",
+ "MATERIAL",
+ 1300008
+ ],
+ "class_p_char_204_platnm": [
+ "class_p_char_204_platnm",
+ "class_p_char_204_platnm",
+ "白金的中坚信物",
+ "MATERIAL",
+ 1300009
+ ],
+ "白金的中坚信物": [
+ "class_p_char_204_platnm",
+ "class_p_char_204_platnm",
+ "白金的中坚信物",
+ "MATERIAL",
+ 1300009
+ ],
+ "class_p_char_219_meteo": [
+ "class_p_char_219_meteo",
+ "class_p_char_219_meteo",
+ "陨星的中坚信物",
+ "MATERIAL",
+ 1300010
+ ],
+ "陨星的中坚信物": [
+ "class_p_char_219_meteo",
+ "class_p_char_219_meteo",
+ "陨星的中坚信物",
+ "MATERIAL",
+ 1300010
+ ],
+ "class_p_char_166_skfire": [
+ "class_p_char_166_skfire",
+ "class_p_char_166_skfire",
+ "天火的中坚信物",
+ "MATERIAL",
+ 1300012
+ ],
+ "天火的中坚信物": [
+ "class_p_char_166_skfire",
+ "class_p_char_166_skfire",
+ "天火的中坚信物",
+ "MATERIAL",
+ 1300012
+ ],
+ "class_p_char_242_otter": [
+ "class_p_char_242_otter",
+ "class_p_char_242_otter",
+ "梅尔的中坚信物",
+ "MATERIAL",
+ 1300013
+ ],
+ "梅尔的中坚信物": [
+ "class_p_char_242_otter",
+ "class_p_char_242_otter",
+ "梅尔的中坚信物",
+ "MATERIAL",
+ 1300013
+ ],
+ "class_p_char_108_silent": [
+ "class_p_char_108_silent",
+ "class_p_char_108_silent",
+ "赫默的中坚信物",
+ "MATERIAL",
+ 1300014
+ ],
+ "赫默的中坚信物": [
+ "class_p_char_108_silent",
+ "class_p_char_108_silent",
+ "赫默的中坚信物",
+ "MATERIAL",
+ 1300014
+ ],
+ "class_p_char_171_bldsk": [
+ "class_p_char_171_bldsk",
+ "class_p_char_171_bldsk",
+ "华法琳的中坚信物",
+ "MATERIAL",
+ 1300015
+ ],
+ "华法琳的中坚信物": [
+ "class_p_char_171_bldsk",
+ "class_p_char_171_bldsk",
+ "华法琳的中坚信物",
+ "MATERIAL",
+ 1300015
+ ],
+ "class_p_char_148_nearl": [
+ "class_p_char_148_nearl",
+ "class_p_char_148_nearl",
+ "临光的中坚信物",
+ "MATERIAL",
+ 1300016
+ ],
+ "临光的中坚信物": [
+ "class_p_char_148_nearl",
+ "class_p_char_148_nearl",
+ "临光的中坚信物",
+ "MATERIAL",
+ 1300016
+ ],
+ "class_p_char_144_red": [
+ "class_p_char_144_red",
+ "class_p_char_144_red",
+ "红的中坚信物",
+ "MATERIAL",
+ 1300017
+ ],
+ "红的中坚信物": [
+ "class_p_char_144_red",
+ "class_p_char_144_red",
+ "红的中坚信物",
+ "MATERIAL",
+ 1300017
+ ],
+ "class_p_char_107_liskam": [
+ "class_p_char_107_liskam",
+ "class_p_char_107_liskam",
+ "雷蛇的中坚信物",
+ "MATERIAL",
+ 1300018
+ ],
+ "雷蛇的中坚信物": [
+ "class_p_char_107_liskam",
+ "class_p_char_107_liskam",
+ "雷蛇的中坚信物",
+ "MATERIAL",
+ 1300018
+ ],
+ "class_p_char_201_moeshd": [
+ "class_p_char_201_moeshd",
+ "class_p_char_201_moeshd",
+ "可颂的中坚信物",
+ "MATERIAL",
+ 1300019
+ ],
+ "可颂的中坚信物": [
+ "class_p_char_201_moeshd",
+ "class_p_char_201_moeshd",
+ "可颂的中坚信物",
+ "MATERIAL",
+ 1300019
+ ],
+ "class_p_char_145_prove": [
+ "class_p_char_145_prove",
+ "class_p_char_145_prove",
+ "普罗旺斯的中坚信物",
+ "MATERIAL",
+ 1300021
+ ],
+ "普罗旺斯的中坚信物": [
+ "class_p_char_145_prove",
+ "class_p_char_145_prove",
+ "普罗旺斯的中坚信物",
+ "MATERIAL",
+ 1300021
+ ],
+ "class_p_char_158_milu": [
+ "class_p_char_158_milu",
+ "class_p_char_158_milu",
+ "守林人的中坚信物",
+ "MATERIAL",
+ 1300022
+ ],
+ "守林人的中坚信物": [
+ "class_p_char_158_milu",
+ "class_p_char_158_milu",
+ "守林人的中坚信物",
+ "MATERIAL",
+ 1300022
+ ],
+ "class_p_char_173_slchan": [
+ "class_p_char_173_slchan",
+ "class_p_char_173_slchan",
+ "崖心的中坚信物",
+ "MATERIAL",
+ 1300023
+ ],
+ "崖心的中坚信物": [
+ "class_p_char_173_slchan",
+ "class_p_char_173_slchan",
+ "崖心的中坚信物",
+ "MATERIAL",
+ 1300023
+ ],
+ "class_p_char_174_slbell": [
+ "class_p_char_174_slbell",
+ "class_p_char_174_slbell",
+ "初雪的中坚信物",
+ "MATERIAL",
+ 1300024
+ ],
+ "初雪的中坚信物": [
+ "class_p_char_174_slbell",
+ "class_p_char_174_slbell",
+ "初雪的中坚信物",
+ "MATERIAL",
+ 1300024
+ ],
+ "class_p_char_195_glassb": [
+ "class_p_char_195_glassb",
+ "class_p_char_195_glassb",
+ "真理的中坚信物",
+ "MATERIAL",
+ 1300025
+ ],
+ "真理的中坚信物": [
+ "class_p_char_195_glassb",
+ "class_p_char_195_glassb",
+ "真理的中坚信物",
+ "MATERIAL",
+ 1300025
+ ],
+ "class_p_char_101_sora": [
+ "class_p_char_101_sora",
+ "class_p_char_101_sora",
+ "空的中坚信物",
+ "MATERIAL",
+ 1300026
+ ],
+ "空的中坚信物": [
+ "class_p_char_101_sora",
+ "class_p_char_101_sora",
+ "空的中坚信物",
+ "MATERIAL",
+ 1300026
+ ],
+ "class_p_char_215_mantic": [
+ "class_p_char_215_mantic",
+ "class_p_char_215_mantic",
+ "狮蝎的中坚信物",
+ "MATERIAL",
+ 1300027
+ ],
+ "狮蝎的中坚信物": [
+ "class_p_char_215_mantic",
+ "class_p_char_215_mantic",
+ "狮蝎的中坚信物",
+ "MATERIAL",
+ 1300027
+ ],
+ "class_p_char_241_panda": [
+ "class_p_char_241_panda",
+ "class_p_char_241_panda",
+ "食铁兽的中坚信物",
+ "MATERIAL",
+ 1300028
+ ],
+ "食铁兽的中坚信物": [
+ "class_p_char_241_panda",
+ "class_p_char_241_panda",
+ "食铁兽的中坚信物",
+ "MATERIAL",
+ 1300028
+ ],
+ "class_p_char_164_nightm": [
+ "class_p_char_164_nightm",
+ "class_p_char_164_nightm",
+ "夜魔的中坚信物",
+ "MATERIAL",
+ 1300030
+ ],
+ "夜魔的中坚信物": [
+ "class_p_char_164_nightm",
+ "class_p_char_164_nightm",
+ "夜魔的中坚信物",
+ "MATERIAL",
+ 1300030
+ ],
+ "class_p_char_308_swire": [
+ "class_p_char_308_swire",
+ "class_p_char_308_swire",
+ "诗怀雅的中坚信物",
+ "MATERIAL",
+ 1300031
+ ],
+ "诗怀雅的中坚信物": [
+ "class_p_char_308_swire",
+ "class_p_char_308_swire",
+ "诗怀雅的中坚信物",
+ "MATERIAL",
+ 1300031
+ ],
+ "class_p_char_326_glacus": [
+ "class_p_char_326_glacus",
+ "class_p_char_326_glacus",
+ "格劳克斯的中坚信物",
+ "MATERIAL",
+ 1300034
+ ],
+ "格劳克斯的中坚信物": [
+ "class_p_char_326_glacus",
+ "class_p_char_326_glacus",
+ "格劳克斯的中坚信物",
+ "MATERIAL",
+ 1300034
+ ],
+ "class_p_char_274_astesi": [
+ "class_p_char_274_astesi",
+ "class_p_char_274_astesi",
+ "星极的中坚信物",
+ "MATERIAL",
+ 1300032
+ ],
+ "星极的中坚信物": [
+ "class_p_char_274_astesi",
+ "class_p_char_274_astesi",
+ "星极的中坚信物",
+ "MATERIAL",
+ 1300032
+ ],
+ "class_p_char_279_excu": [
+ "class_p_char_279_excu",
+ "class_p_char_279_excu",
+ "送葬人的中坚信物",
+ "MATERIAL",
+ 1300037
+ ],
+ "送葬人的中坚信物": [
+ "class_p_char_279_excu",
+ "class_p_char_279_excu",
+ "送葬人的中坚信物",
+ "MATERIAL",
+ 1300037
+ ],
+ "class_p_char_243_waaifu": [
+ "class_p_char_243_waaifu",
+ "class_p_char_243_waaifu",
+ "槐琥的中坚信物",
+ "MATERIAL",
+ 1300040
+ ],
+ "槐琥的中坚信物": [
+ "class_p_char_243_waaifu",
+ "class_p_char_243_waaifu",
+ "槐琥的中坚信物",
+ "MATERIAL",
+ 1300040
+ ],
+ "class_p_char_261_sddrag": [
+ "class_p_char_261_sddrag",
+ "class_p_char_261_sddrag",
+ "苇草的中坚信物",
+ "MATERIAL",
+ 1300038
+ ],
+ "苇草的中坚信物": [
+ "class_p_char_261_sddrag",
+ "class_p_char_261_sddrag",
+ "苇草的中坚信物",
+ "MATERIAL",
+ 1300038
+ ],
+ "class_p_char_356_broca": [
+ "class_p_char_356_broca",
+ "class_p_char_356_broca",
+ "布洛卡的中坚信物",
+ "MATERIAL",
+ 1300039
+ ],
+ "布洛卡的中坚信物": [
+ "class_p_char_356_broca",
+ "class_p_char_356_broca",
+ "布洛卡的中坚信物",
+ "MATERIAL",
+ 1300039
+ ],
+ "class_p_char_367_swllow": [
+ "class_p_char_367_swllow",
+ "class_p_char_367_swllow",
+ "灰喉的中坚信物",
+ "MATERIAL",
+ 1300042
+ ],
+ "灰喉的中坚信物": [
+ "class_p_char_367_swllow",
+ "class_p_char_367_swllow",
+ "灰喉的中坚信物",
+ "MATERIAL",
+ 1300042
+ ],
+ "class_p_char_226_hmau": [
+ "class_p_char_226_hmau",
+ "class_p_char_226_hmau",
+ "吽的中坚信物",
+ "MATERIAL",
+ 1300043
+ ],
+ "吽的中坚信物": [
+ "class_p_char_226_hmau",
+ "class_p_char_226_hmau",
+ "吽的中坚信物",
+ "MATERIAL",
+ 1300043
+ ],
+ "class_p_char_306_leizi": [
+ "class_p_char_306_leizi",
+ "class_p_char_306_leizi",
+ "惊蛰的中坚信物",
+ "MATERIAL",
+ 1300045
+ ],
+ "惊蛰的中坚信物": [
+ "class_p_char_306_leizi",
+ "class_p_char_306_leizi",
+ "惊蛰的中坚信物",
+ "MATERIAL",
+ 1300045
+ ],
+ "class_p_char_379_sesa": [
+ "class_p_char_379_sesa",
+ "class_p_char_379_sesa",
+ "慑砂的中坚信物",
+ "MATERIAL",
+ 1300046
+ ],
+ "慑砂的中坚信物": [
+ "class_p_char_379_sesa",
+ "class_p_char_379_sesa",
+ "慑砂的中坚信物",
+ "MATERIAL",
+ 1300046
+ ],
+ "class_p_char_254_vodfox": [
+ "class_p_char_254_vodfox",
+ "class_p_char_254_vodfox",
+ "巫恋的中坚信物",
+ "MATERIAL",
+ 1300048
+ ],
+ "巫恋的中坚信物": [
+ "class_p_char_254_vodfox",
+ "class_p_char_254_vodfox",
+ "巫恋的中坚信物",
+ "MATERIAL",
+ 1300048
+ ],
+ "class_p_char_401_elysm": [
+ "class_p_char_401_elysm",
+ "class_p_char_401_elysm",
+ "极境的中坚信物",
+ "MATERIAL",
+ 1300049
+ ],
+ "极境的中坚信物": [
+ "class_p_char_401_elysm",
+ "class_p_char_401_elysm",
+ "极境的中坚信物",
+ "MATERIAL",
+ 1300049
+ ],
+ "class_p_char_378_asbest": [
+ "class_p_char_378_asbest",
+ "class_p_char_378_asbest",
+ "石棉的中坚信物",
+ "MATERIAL",
+ 1300050
+ ],
+ "石棉的中坚信物": [
+ "class_p_char_378_asbest",
+ "class_p_char_378_asbest",
+ "石棉的中坚信物",
+ "MATERIAL",
+ 1300050
+ ],
+ "class_p_char_343_tknogi": [
+ "class_p_char_343_tknogi",
+ "class_p_char_343_tknogi",
+ "月禾的中坚信物",
+ "MATERIAL",
+ 1300051
+ ],
+ "月禾的中坚信物": [
+ "class_p_char_343_tknogi",
+ "class_p_char_343_tknogi",
+ "月禾的中坚信物",
+ "MATERIAL",
+ 1300051
+ ],
+ "class_p_char_373_lionhd": [
+ "class_p_char_373_lionhd",
+ "class_p_char_373_lionhd",
+ "莱恩哈特的中坚信物",
+ "MATERIAL",
+ 1300052
+ ],
+ "莱恩哈特的中坚信物": [
+ "class_p_char_373_lionhd",
+ "class_p_char_373_lionhd",
+ "莱恩哈特的中坚信物",
+ "MATERIAL",
+ 1300052
+ ],
+ "class_p_char_294_ayer": [
+ "class_p_char_294_ayer",
+ "class_p_char_294_ayer",
+ "断崖的中坚信物",
+ "MATERIAL",
+ 1300053
+ ],
+ "断崖的中坚信物": [
+ "class_p_char_294_ayer",
+ "class_p_char_294_ayer",
+ "断崖的中坚信物",
+ "MATERIAL",
+ 1300053
+ ],
+ "class_p_char_344_beewax": [
+ "class_p_char_344_beewax",
+ "class_p_char_344_beewax",
+ "蜜蜡的中坚信物",
+ "MATERIAL",
+ 1300054
+ ],
+ "蜜蜡的中坚信物": [
+ "class_p_char_344_beewax",
+ "class_p_char_344_beewax",
+ "蜜蜡的中坚信物",
+ "MATERIAL",
+ 1300054
+ ],
+ "class_p_char_349_chiave": [
+ "class_p_char_349_chiave",
+ "class_p_char_349_chiave",
+ "贾维的中坚信物",
+ "MATERIAL",
+ 1300055
+ ],
+ "贾维的中坚信物": [
+ "class_p_char_349_chiave",
+ "class_p_char_349_chiave",
+ "贾维的中坚信物",
+ "MATERIAL",
+ 1300055
+ ],
+ "class_p_char_218_cuttle": [
+ "class_p_char_218_cuttle",
+ "class_p_char_218_cuttle",
+ "安哲拉的中坚信物",
+ "MATERIAL",
+ 1300056
+ ],
+ "安哲拉的中坚信物": [
+ "class_p_char_218_cuttle",
+ "class_p_char_218_cuttle",
+ "安哲拉的中坚信物",
+ "MATERIAL",
+ 1300056
+ ],
+ "class_p_char_415_flint": [
+ "class_p_char_415_flint",
+ "class_p_char_415_flint",
+ "燧石的中坚信物",
+ "MATERIAL",
+ 1300057
+ ],
+ "燧石的中坚信物": [
+ "class_p_char_415_flint",
+ "class_p_char_415_flint",
+ "燧石的中坚信物",
+ "MATERIAL",
+ 1300057
+ ],
+ "class_p_char_365_aprl": [
+ "class_p_char_365_aprl",
+ "class_p_char_365_aprl",
+ "四月的中坚信物",
+ "MATERIAL",
+ 1300058
+ ],
+ "四月的中坚信物": [
+ "class_p_char_365_aprl",
+ "class_p_char_365_aprl",
+ "四月的中坚信物",
+ "MATERIAL",
+ 1300058
+ ],
+ "class_p_char_346_aosta": [
+ "class_p_char_346_aosta",
+ "class_p_char_346_aosta",
+ "奥斯塔的中坚信物",
+ "MATERIAL",
+ 1300059
+ ],
+ "奥斯塔的中坚信物": [
+ "class_p_char_346_aosta",
+ "class_p_char_346_aosta",
+ "奥斯塔的中坚信物",
+ "MATERIAL",
+ 1300059
+ ],
+ "class_p_char_436_whispr": [
+ "class_p_char_436_whispr",
+ "class_p_char_436_whispr",
+ "絮雨的中坚信物",
+ "MATERIAL",
+ 1300060
+ ],
+ "絮雨的中坚信物": [
+ "class_p_char_436_whispr",
+ "class_p_char_436_whispr",
+ "絮雨的中坚信物",
+ "MATERIAL",
+ 1300060
+ ],
+ "class_p_char_214_kafka": [
+ "class_p_char_214_kafka",
+ "class_p_char_214_kafka",
+ "卡夫卡的中坚信物",
+ "MATERIAL",
+ 1300061
+ ],
+ "卡夫卡的中坚信物": [
+ "class_p_char_214_kafka",
+ "class_p_char_214_kafka",
+ "卡夫卡的中坚信物",
+ "MATERIAL",
+ 1300061
+ ],
+ "class_p_char_338_iris": [
+ "class_p_char_338_iris",
+ "class_p_char_338_iris",
+ "爱丽丝的中坚信物",
+ "MATERIAL",
+ 1300062
+ ],
+ "爱丽丝的中坚信物": [
+ "class_p_char_338_iris",
+ "class_p_char_338_iris",
+ "爱丽丝的中坚信物",
+ "MATERIAL",
+ 1300062
+ ],
+ "class_p_char_455_nothin": [
+ "class_p_char_455_nothin",
+ "class_p_char_455_nothin",
+ "乌有的中坚信物",
+ "MATERIAL",
+ 1300063
+ ],
+ "乌有的中坚信物": [
+ "class_p_char_455_nothin",
+ "class_p_char_455_nothin",
+ "乌有的中坚信物",
+ "MATERIAL",
+ 1300063
+ ],
+ "class_p_char_363_toddi": [
+ "class_p_char_363_toddi",
+ "class_p_char_363_toddi",
+ "熔泉的中坚信物",
+ "MATERIAL",
+ 1300064
+ ],
+ "熔泉的中坚信物": [
+ "class_p_char_363_toddi",
+ "class_p_char_363_toddi",
+ "熔泉的中坚信物",
+ "MATERIAL",
+ 1300064
+ ],
+ "class_p_char_103_angel": [
+ "class_p_char_103_angel",
+ "class_p_char_103_angel",
+ "能天使的中坚信物",
+ "MATERIAL",
+ 1200001
+ ],
+ "能天使的中坚信物": [
+ "class_p_char_103_angel",
+ "class_p_char_103_angel",
+ "能天使的中坚信物",
+ "MATERIAL",
+ 1200001
+ ],
+ "class_p_char_112_siege": [
+ "class_p_char_112_siege",
+ "class_p_char_112_siege",
+ "推进之王的中坚信物",
+ "MATERIAL",
+ 1200002
+ ],
+ "推进之王的中坚信物": [
+ "class_p_char_112_siege",
+ "class_p_char_112_siege",
+ "推进之王的中坚信物",
+ "MATERIAL",
+ 1200002
+ ],
+ "class_p_char_134_ifrit": [
+ "class_p_char_134_ifrit",
+ "class_p_char_134_ifrit",
+ "伊芙利特的中坚信物",
+ "MATERIAL",
+ 1200003
+ ],
+ "伊芙利特的中坚信物": [
+ "class_p_char_134_ifrit",
+ "class_p_char_134_ifrit",
+ "伊芙利特的中坚信物",
+ "MATERIAL",
+ 1200003
+ ],
+ "class_p_char_180_amgoat": [
+ "class_p_char_180_amgoat",
+ "class_p_char_180_amgoat",
+ "艾雅法拉的中坚信物",
+ "MATERIAL",
+ 1200004
+ ],
+ "艾雅法拉的中坚信物": [
+ "class_p_char_180_amgoat",
+ "class_p_char_180_amgoat",
+ "艾雅法拉的中坚信物",
+ "MATERIAL",
+ 1200004
+ ],
+ "class_p_char_291_aglina": [
+ "class_p_char_291_aglina",
+ "class_p_char_291_aglina",
+ "安洁莉娜的中坚信物",
+ "MATERIAL",
+ 1200005
+ ],
+ "安洁莉娜的中坚信物": [
+ "class_p_char_291_aglina",
+ "class_p_char_291_aglina",
+ "安洁莉娜的中坚信物",
+ "MATERIAL",
+ 1200005
+ ],
+ "class_p_char_147_shining": [
+ "class_p_char_147_shining",
+ "class_p_char_147_shining",
+ "闪灵的中坚信物",
+ "MATERIAL",
+ 1200006
+ ],
+ "闪灵的中坚信物": [
+ "class_p_char_147_shining",
+ "class_p_char_147_shining",
+ "闪灵的中坚信物",
+ "MATERIAL",
+ 1200006
+ ],
+ "class_p_char_179_cgbird": [
+ "class_p_char_179_cgbird",
+ "class_p_char_179_cgbird",
+ "夜莺的中坚信物",
+ "MATERIAL",
+ 1200007
+ ],
+ "夜莺的中坚信物": [
+ "class_p_char_179_cgbird",
+ "class_p_char_179_cgbird",
+ "夜莺的中坚信物",
+ "MATERIAL",
+ 1200007
+ ],
+ "class_p_char_136_hsguma": [
+ "class_p_char_136_hsguma",
+ "class_p_char_136_hsguma",
+ "星熊的中坚信物",
+ "MATERIAL",
+ 1200008
+ ],
+ "星熊的中坚信物": [
+ "class_p_char_136_hsguma",
+ "class_p_char_136_hsguma",
+ "星熊的中坚信物",
+ "MATERIAL",
+ 1200008
+ ],
+ "class_p_char_202_demkni": [
+ "class_p_char_202_demkni",
+ "class_p_char_202_demkni",
+ "塞雷娅的中坚信物",
+ "MATERIAL",
+ 1200009
+ ],
+ "塞雷娅的中坚信物": [
+ "class_p_char_202_demkni",
+ "class_p_char_202_demkni",
+ "塞雷娅的中坚信物",
+ "MATERIAL",
+ 1200009
+ ],
+ "class_p_char_172_svrash": [
+ "class_p_char_172_svrash",
+ "class_p_char_172_svrash",
+ "银灰的中坚信物",
+ "MATERIAL",
+ 1200010
+ ],
+ "银灰的中坚信物": [
+ "class_p_char_172_svrash",
+ "class_p_char_172_svrash",
+ "银灰的中坚信物",
+ "MATERIAL",
+ 1200010
+ ],
+ "class_p_char_263_skadi": [
+ "class_p_char_263_skadi",
+ "class_p_char_263_skadi",
+ "斯卡蒂的中坚信物",
+ "MATERIAL",
+ 1200011
+ ],
+ "斯卡蒂的中坚信物": [
+ "class_p_char_263_skadi",
+ "class_p_char_263_skadi",
+ "斯卡蒂的中坚信物",
+ "MATERIAL",
+ 1200011
+ ],
+ "class_p_char_010_chen": [
+ "class_p_char_010_chen",
+ "class_p_char_010_chen",
+ "陈的中坚信物",
+ "MATERIAL",
+ 1200012
+ ],
+ "陈的中坚信物": [
+ "class_p_char_010_chen",
+ "class_p_char_010_chen",
+ "陈的中坚信物",
+ "MATERIAL",
+ 1200012
+ ],
+ "class_p_char_340_shwaz": [
+ "class_p_char_340_shwaz",
+ "class_p_char_340_shwaz",
+ "黑的中坚信物",
+ "MATERIAL",
+ 1200013
+ ],
+ "黑的中坚信物": [
+ "class_p_char_340_shwaz",
+ "class_p_char_340_shwaz",
+ "黑的中坚信物",
+ "MATERIAL",
+ 1200013
+ ],
+ "class_p_char_188_helage": [
+ "class_p_char_188_helage",
+ "class_p_char_188_helage",
+ "赫拉格的中坚信物",
+ "MATERIAL",
+ 1200014
+ ],
+ "赫拉格的中坚信物": [
+ "class_p_char_188_helage",
+ "class_p_char_188_helage",
+ "赫拉格的中坚信物",
+ "MATERIAL",
+ 1200014
+ ],
+ "class_p_char_248_mgllan": [
+ "class_p_char_248_mgllan",
+ "class_p_char_248_mgllan",
+ "麦哲伦的中坚信物",
+ "MATERIAL",
+ 1200015
+ ],
+ "麦哲伦的中坚信物": [
+ "class_p_char_248_mgllan",
+ "class_p_char_248_mgllan",
+ "麦哲伦的中坚信物",
+ "MATERIAL",
+ 1200015
+ ],
+ "class_p_char_213_mostma": [
+ "class_p_char_213_mostma",
+ "class_p_char_213_mostma",
+ "莫斯提马的中坚信物",
+ "MATERIAL",
+ 1200016
+ ],
+ "莫斯提马的中坚信物": [
+ "class_p_char_213_mostma",
+ "class_p_char_213_mostma",
+ "莫斯提马的中坚信物",
+ "MATERIAL",
+ 1200016
+ ],
+ "class_p_char_017_huang": [
+ "class_p_char_017_huang",
+ "class_p_char_017_huang",
+ "煌的中坚信物",
+ "MATERIAL",
+ 1200019
+ ],
+ "煌的中坚信物": [
+ "class_p_char_017_huang",
+ "class_p_char_017_huang",
+ "煌的中坚信物",
+ "MATERIAL",
+ 1200019
+ ],
+ "class_p_char_225_haak": [
+ "class_p_char_225_haak",
+ "class_p_char_225_haak",
+ "阿的中坚信物",
+ "MATERIAL",
+ 1200017
+ ],
+ "阿的中坚信物": [
+ "class_p_char_225_haak",
+ "class_p_char_225_haak",
+ "阿的中坚信物",
+ "MATERIAL",
+ 1200017
+ ],
+ "class_p_char_2013_cerber": [
+ "class_p_char_2013_cerber",
+ "class_p_char_2013_cerber",
+ "刻俄柏的中坚信物",
+ "MATERIAL",
+ 1200020
+ ],
+ "刻俄柏的中坚信物": [
+ "class_p_char_2013_cerber",
+ "class_p_char_2013_cerber",
+ "刻俄柏的中坚信物",
+ "MATERIAL",
+ 1200020
+ ],
+ "class_p_char_222_bpipe": [
+ "class_p_char_222_bpipe",
+ "class_p_char_222_bpipe",
+ "风笛的中坚信物",
+ "MATERIAL",
+ 1200021
+ ],
+ "风笛的中坚信物": [
+ "class_p_char_222_bpipe",
+ "class_p_char_222_bpipe",
+ "风笛的中坚信物",
+ "MATERIAL",
+ 1200021
+ ],
+ "class_p_char_250_phatom": [
+ "class_p_char_250_phatom",
+ "class_p_char_250_phatom",
+ "傀影的中坚信物",
+ "MATERIAL",
+ 1200022
+ ],
+ "傀影的中坚信物": [
+ "class_p_char_250_phatom",
+ "class_p_char_250_phatom",
+ "傀影的中坚信物",
+ "MATERIAL",
+ 1200022
+ ],
+ "class_p_char_400_weedy": [
+ "class_p_char_400_weedy",
+ "class_p_char_400_weedy",
+ "温蒂的中坚信物",
+ "MATERIAL",
+ 1200023
+ ],
+ "温蒂的中坚信物": [
+ "class_p_char_400_weedy",
+ "class_p_char_400_weedy",
+ "温蒂的中坚信物",
+ "MATERIAL",
+ 1200023
+ ],
+ "class_p_char_197_poca": [
+ "class_p_char_197_poca",
+ "class_p_char_197_poca",
+ "早露的中坚信物",
+ "MATERIAL",
+ 1200024
+ ],
+ "早露的中坚信物": [
+ "class_p_char_197_poca",
+ "class_p_char_197_poca",
+ "早露的中坚信物",
+ "MATERIAL",
+ 1200024
+ ],
+ "class_p_char_358_lisa": [
+ "class_p_char_358_lisa",
+ "class_p_char_358_lisa",
+ "铃兰的中坚信物",
+ "MATERIAL",
+ 1200025
+ ],
+ "铃兰的中坚信物": [
+ "class_p_char_358_lisa",
+ "class_p_char_358_lisa",
+ "铃兰的中坚信物",
+ "MATERIAL",
+ 1200025
+ ],
+ "class_p_char_293_thorns": [
+ "class_p_char_293_thorns",
+ "class_p_char_293_thorns",
+ "棘刺的中坚信物",
+ "MATERIAL",
+ 1200026
+ ],
+ "棘刺的中坚信物": [
+ "class_p_char_293_thorns",
+ "class_p_char_293_thorns",
+ "棘刺的中坚信物",
+ "MATERIAL",
+ 1200026
+ ],
+ "class_p_char_416_zumama": [
+ "class_p_char_416_zumama",
+ "class_p_char_416_zumama",
+ "森蚺的中坚信物",
+ "MATERIAL",
+ 1200027
+ ],
+ "森蚺的中坚信物": [
+ "class_p_char_416_zumama",
+ "class_p_char_416_zumama",
+ "森蚺的中坚信物",
+ "MATERIAL",
+ 1200027
+ ],
+ "class_p_char_350_surtr": [
+ "class_p_char_350_surtr",
+ "class_p_char_350_surtr",
+ "史尔特尔的中坚信物",
+ "MATERIAL",
+ 1200028
+ ],
+ "史尔特尔的中坚信物": [
+ "class_p_char_350_surtr",
+ "class_p_char_350_surtr",
+ "史尔特尔的中坚信物",
+ "MATERIAL",
+ 1200028
+ ],
+ "class_p_char_423_blemsh": [
+ "class_p_char_423_blemsh",
+ "class_p_char_423_blemsh",
+ "瑕光的中坚信物",
+ "MATERIAL",
+ 1200029
+ ],
+ "瑕光的中坚信物": [
+ "class_p_char_423_blemsh",
+ "class_p_char_423_blemsh",
+ "瑕光的中坚信物",
+ "MATERIAL",
+ 1200029
+ ],
+ "class_p_char_311_mudrok": [
+ "class_p_char_311_mudrok",
+ "class_p_char_311_mudrok",
+ "泥岩的中坚信物",
+ "MATERIAL",
+ 1200030
+ ],
+ "泥岩的中坚信物": [
+ "class_p_char_311_mudrok",
+ "class_p_char_311_mudrok",
+ "泥岩的中坚信物",
+ "MATERIAL",
+ 1200030
+ ],
+ "class_p_char_264_f12yin": [
+ "class_p_char_264_f12yin",
+ "class_p_char_264_f12yin",
+ "山的中坚信物",
+ "MATERIAL",
+ 1200031
+ ],
+ "山的中坚信物": [
+ "class_p_char_264_f12yin",
+ "class_p_char_264_f12yin",
+ "山的中坚信物",
+ "MATERIAL",
+ 1200031
+ ],
+ "class_p_char_332_archet": [
+ "class_p_char_332_archet",
+ "class_p_char_332_archet",
+ "空弦的中坚信物",
+ "MATERIAL",
+ 1200032
+ ],
+ "空弦的中坚信物": [
+ "class_p_char_332_archet",
+ "class_p_char_332_archet",
+ "空弦的中坚信物",
+ "MATERIAL",
+ 1200032
+ ],
+ "class_p_char_362_saga": [
+ "class_p_char_362_saga",
+ "class_p_char_362_saga",
+ "嵯峨的中坚信物",
+ "MATERIAL",
+ 1200033
+ ],
+ "嵯峨的中坚信物": [
+ "class_p_char_362_saga",
+ "class_p_char_362_saga",
+ "嵯峨的中坚信物",
+ "MATERIAL",
+ 1200033
+ ],
+ "class_p_char_472_pasngr": [
+ "class_p_char_472_pasngr",
+ "class_p_char_472_pasngr",
+ "异客的中坚信物",
+ "MATERIAL",
+ 1200034
+ ],
+ "异客的中坚信物": [
+ "class_p_char_472_pasngr",
+ "class_p_char_472_pasngr",
+ "异客的中坚信物",
+ "MATERIAL",
+ 1200034
+ ],
+ "classic_normal_ticket": [
+ "classic_normal_ticket",
+ "classic_normal_ticket",
+ "通用凭证",
+ "NORMAL",
+ 10006
+ ],
+ "通用凭证": [
+ "classic_normal_ticket",
+ "classic_normal_ticket",
+ "通用凭证",
+ "NORMAL",
+ 10006
+ ],
+ "classic_gacha": [
+ "classic_gacha",
+ "classic_gacha",
+ "中坚寻访凭证",
+ "NORMAL",
+ 40011
+ ],
+ "中坚寻访凭证": [
+ "classic_gacha",
+ "classic_gacha",
+ "中坚寻访凭证",
+ "NORMAL",
+ 40011
+ ],
+ "classic_gacha_10": [
+ "classic_gacha_10",
+ "classic_gacha_10",
+ "十连中坚寻访凭证",
+ "NORMAL",
+ 40001
+ ],
+ "十连中坚寻访凭证": [
+ "classic_gacha_10",
+ "classic_gacha_10",
+ "十连中坚寻访凭证",
+ "NORMAL",
+ 40001
+ ],
+ "premium_material_issue_voucher": [
+ "premium_material_issue_voucher",
+ "premium_material_issue_voucher",
+ "特级材料提货券",
+ "CONSUME",
+ 30200
+ ],
+ "advanced_material_issue_voucher": [
+ "advanced_material_issue_voucher",
+ "advanced_material_issue_voucher",
+ "高级材料提货券",
+ "CONSUME",
+ 30201
+ ]
+}
\ No newline at end of file
diff --git a/arknights_mower/data/ocr.json b/arknights_mower/data/ocr.json
index df08edb30..4edcd75f6 100644
--- a/arknights_mower/data/ocr.json
+++ b/arknights_mower/data/ocr.json
@@ -3,10 +3,19 @@
"HU男易": "削弱",
"背身易": "削弱",
"尚身易": "削弱",
+ "尚男易": "削弱",
"尚小深深": "削弱",
+ "新": "新手",
+ "新羊": "新手",
"技巧概要卷1": "技巧概要·卷1",
"技巧概要卷2": "技巧概要·卷2",
+ "龙门市": "龙门币",
"医疗千员": "医疗干员",
+ "医疗千千员": "医疗干员",
+ "医疗干干员": "医疗干员",
+ "医疗干千员": "医疗干员",
+ "医疗午员": "医疗干员",
+ "咴疗多": "医疗干员",
"先锋千员": "先锋干员",
"近卫千员": "近卫干员",
"术师千员": "术师干员",
@@ -18,14 +27,30 @@
"资深千员": "资深干员",
"聚酸醋": "聚酸酯",
"醋原料": "酯原料",
- "龙门市": "龙门币",
"酮爆集": "酮凝集",
"雨凝集": "酮凝集",
+ "米营": "糖",
+ "米唐": "糖",
+ "特唐": "糖",
+ "特惠": "糖",
"租击干员": "狙击干员",
"612F": "12F",
"S12F": "12F",
"CastIe3": "Castle-3",
"Castle3": "Castle-3",
"Lancet2": "Lancet-2",
- "THRMEX": "THRM-EX"
-}
\ No newline at end of file
+ "THRMEX": "THRM-EX",
+ "U-Officia": "U-Official",
+ "UOfficial": "U-Official",
+ "铅跟": "铅踝",
+ "铅课": "铅踝",
+ "铅躁": "铅踝",
+ "青积": "青枳",
+ "苍替": "苍苔",
+ "刻力": "刻刀",
+ "泰拉大陆调查": "泰拉大陆调查团",
+ "子": "孑",
+ "森": "森蚺",
+ "屯艾雅法拉": "纯烬艾雅法拉",
+ "赤赫赤默": "赫默"
+}
diff --git a/arknights_mower/data/recruit.json b/arknights_mower/data/recruit.json
index 3d6b5f55d..032fb207d 100644
--- a/arknights_mower/data/recruit.json
+++ b/arknights_mower/data/recruit.json
@@ -1,1078 +1,1439 @@
{
- "Lancet-2": {
+ "char_285_medic2": {
"name": "Lancet-2",
"stars": 1,
"tags": [
- "医疗干员",
- "远程位",
+ "支援机械",
"治疗",
- "支援机械"
+ "远程位",
+ "医疗干员"
]
},
- "Castle-3": {
+ "char_286_cast3": {
"name": "Castle-3",
"stars": 1,
"tags": [
- "近卫干员",
+ "支援机械",
+ "支援",
+ "近战位",
+ "近卫干员"
+ ]
+ },
+ "char_376_therex": {
+ "name": "THRM-EX",
+ "stars": 1,
+ "tags": [
+ "支援机械",
+ "爆发",
"近战位",
+ "特种干员"
+ ]
+ },
+ "char_4000_jnight": {
+ "name": "正义骑士号",
+ "stars": 1,
+ "tags": [
+ "支援机械",
"支援",
- "支援机械"
+ "远程位",
+ "狙击干员"
]
},
- "夜刀": {
+ "char_4093_frston": {
+ "name": "Friston-3",
+ "stars": 1,
+ "tags": [
+ "支援机械",
+ "防护",
+ "近战位",
+ "重装干员"
+ ]
+ },
+ "char_4136_phonor": {
+ "name": "PhonoR-0",
+ "stars": 1,
+ "tags": [
+ "支援机械",
+ "元素",
+ "远程位",
+ "辅助干员"
+ ]
+ },
+ "char_502_nblade": {
"name": "夜刀",
"stars": 2,
"tags": [
- "先锋干员",
+ "新手",
"近战位",
- "新手"
+ "先锋干员"
]
},
- "黑角": {
+ "char_500_noirc": {
"name": "黑角",
"stars": 2,
"tags": [
- "重装干员",
+ "新手",
"近战位",
- "新手"
+ "重装干员"
]
},
- "巡林者": {
+ "char_503_rang": {
"name": "巡林者",
"stars": 2,
"tags": [
- "狙击干员",
+ "新手",
"远程位",
- "新手"
+ "狙击干员"
]
},
- "杜林": {
+ "char_501_durin": {
"name": "杜林",
"stars": 2,
"tags": [
- "术师干员",
+ "新手",
"远程位",
- "新手"
+ "术师干员"
]
},
- "12F": {
+ "char_009_12fce": {
"name": "12F",
"stars": 2,
"tags": [
- "术师干员",
+ "新手",
"远程位",
- "新手"
+ "术师干员"
]
},
- "芬": {
+ "char_123_fang": {
"name": "芬",
"stars": 3,
"tags": [
- "先锋干员",
+ "费用回复",
"近战位",
- "费用回复"
+ "先锋干员"
]
},
- "香草": {
+ "char_240_wyvern": {
"name": "香草",
"stars": 3,
"tags": [
- "先锋干员",
+ "费用回复",
"近战位",
- "费用回复"
+ "先锋干员"
]
},
- "翎羽": {
+ "char_192_falco": {
"name": "翎羽",
"stars": 3,
"tags": [
- "先锋干员",
- "近战位",
"输出",
- "费用回复"
+ "费用回复",
+ "近战位",
+ "先锋干员"
]
},
- "玫兰莎": {
+ "char_208_melan": {
"name": "玫兰莎",
"stars": 3,
"tags": [
- "近卫干员",
- "近战位",
"输出",
- "生存"
+ "生存",
+ "近战位",
+ "近卫干员"
+ ]
+ },
+ "char_281_popka": {
+ "name": "泡普卡",
+ "stars": 3,
+ "tags": [
+ "群攻",
+ "生存",
+ "近战位",
+ "近卫干员"
]
},
- "米格鲁": {
+ "char_122_beagle": {
"name": "米格鲁",
"stars": 3,
"tags": [
- "重装干员",
+ "防护",
+ "近战位",
+ "重装干员"
+ ]
+ },
+ "char_284_spot": {
+ "name": "斑点",
+ "stars": 3,
+ "tags": [
+ "防护",
+ "治疗",
"近战位",
- "防护"
+ "重装干员"
]
},
- "克洛丝": {
+ "char_124_kroos": {
"name": "克洛丝",
"stars": 3,
"tags": [
- "狙击干员",
+ "输出",
"远程位",
- "输出"
+ "狙击干员"
]
},
- "安德切尔": {
+ "char_211_adnach": {
"name": "安德切尔",
"stars": 3,
"tags": [
- "狙击干员",
+ "输出",
"远程位",
- "输出"
+ "狙击干员"
]
},
- "炎熔": {
+ "char_121_lava": {
"name": "炎熔",
"stars": 3,
"tags": [
- "术师干员",
+ "群攻",
"远程位",
- "群攻"
+ "术师干员"
]
},
- "芙蓉": {
+ "char_120_hibisc": {
"name": "芙蓉",
"stars": 3,
"tags": [
- "医疗干员",
+ "治疗",
"远程位",
- "治疗"
+ "医疗干员"
]
},
- "安赛尔": {
+ "char_212_ansel": {
"name": "安赛尔",
"stars": 3,
"tags": [
- "医疗干员",
+ "治疗",
"远程位",
- "治疗"
+ "医疗干员"
]
},
- "史都华德": {
+ "char_210_stward": {
"name": "史都华德",
"stars": 3,
"tags": [
- "术师干员",
+ "输出",
"远程位",
- "输出"
+ "术师干员"
]
},
- "梓兰": {
+ "char_278_orchid": {
"name": "梓兰",
"stars": 3,
"tags": [
- "辅助干员",
+ "减速",
"远程位",
- "减速"
+ "辅助干员"
]
},
- "夜烟": {
+ "char_141_nights": {
"name": "夜烟",
"stars": 4,
"tags": [
- "术师干员",
- "远程位",
"输出",
- "削弱"
+ "削弱",
+ "远程位",
+ "术师干员"
]
},
- "远山": {
+ "char_109_fmout": {
"name": "远山",
"stars": 4,
"tags": [
- "术师干员",
+ "群攻",
"远程位",
- "群攻"
+ "术师干员"
]
},
- "杰西卡": {
- "name": "杰西卡",
+ "char_253_greyy": {
+ "name": "格雷伊",
"stars": 4,
"tags": [
- "狙击干员",
+ "群攻",
+ "减速",
+ "远程位",
+ "术师干员"
+ ]
+ },
+ "char_328_cammou": {
+ "name": "卡达",
+ "stars": 4,
+ "tags": [
+ "输出",
+ "控场",
"远程位",
+ "术师干员"
+ ]
+ },
+ "char_235_jesica": {
+ "name": "杰西卡",
+ "stars": 4,
+ "tags": [
"输出",
- "生存"
+ "生存",
+ "远程位",
+ "狙击干员"
]
},
- "流星": {
+ "char_126_shotst": {
"name": "流星",
"stars": 4,
"tags": [
- "狙击干员",
+ "输出",
+ "削弱",
"远程位",
+ "狙击干员"
+ ]
+ },
+ "char_190_clour": {
+ "name": "红云",
+ "stars": 4,
+ "tags": [
"输出",
- "削弱"
+ "远程位",
+ "狙击干员"
]
},
- "白雪": {
- "name": "白雪",
+ "char_133_mm": {
+ "name": "梅",
"stars": 4,
"tags": [
- "狙击干员",
+ "输出",
+ "减速",
"远程位",
+ "狙击干员"
+ ]
+ },
+ "char_118_yuki": {
+ "name": "白雪",
+ "stars": 4,
+ "tags": [
"群攻",
- "减速"
+ "减速",
+ "远程位",
+ "狙击干员"
+ ]
+ },
+ "char_302_glaze": {
+ "name": "安比尔",
+ "stars": 4,
+ "tags": [
+ "输出",
+ "减速",
+ "远程位",
+ "狙击干员"
+ ]
+ },
+ "char_366_acdrop": {
+ "name": "酸糖",
+ "stars": 4,
+ "tags": [
+ "输出",
+ "远程位",
+ "狙击干员"
]
},
- "清道夫": {
+ "char_149_scave": {
"name": "清道夫",
"stars": 4,
"tags": [
- "先锋干员",
- "近战位",
"费用回复",
- "输出"
+ "输出",
+ "近战位",
+ "先锋干员"
]
},
- "红豆": {
+ "char_290_vigna": {
"name": "红豆",
"stars": 4,
"tags": [
- "先锋干员",
- "近战位",
"输出",
- "费用回复"
+ "费用回复",
+ "近战位",
+ "先锋干员"
]
},
- "杜宾": {
- "name": "杜宾",
+ "char_151_myrtle": {
+ "name": "桃金娘",
"stars": 4,
"tags": [
- "近卫干员",
+ "费用回复",
+ "治疗",
"近战位",
+ "先锋干员"
+ ]
+ },
+ "char_130_doberm": {
+ "name": "杜宾",
+ "stars": 4,
+ "tags": [
"输出",
- "支援"
+ "支援",
+ "近战位",
+ "近卫干员"
]
},
- "缠丸": {
+ "char_289_gyuki": {
"name": "缠丸",
"stars": 4,
"tags": [
- "近卫干员",
- "近战位",
"生存",
- "输出"
+ "输出",
+ "近战位",
+ "近卫干员"
]
},
- "霜叶": {
+ "char_193_frostl": {
"name": "霜叶",
"stars": 4,
"tags": [
- "近卫干员",
- "近战位",
"减速",
- "输出"
+ "输出",
+ "近战位",
+ "近卫干员"
]
},
- "艾丝黛尔": {
+ "char_127_estell": {
"name": "艾丝黛尔",
"stars": 4,
"tags": [
- "近卫干员",
- "近战位",
"群攻",
- "生存"
+ "生存",
+ "近战位",
+ "近卫干员"
]
},
- "慕斯": {
+ "char_185_frncat": {
"name": "慕斯",
"stars": 4,
"tags": [
- "近卫干员",
+ "输出",
+ "近战位",
+ "近卫干员"
+ ]
+ },
+ "char_301_cutter": {
+ "name": "刻刀",
+ "stars": 4,
+ "tags": [
+ "爆发",
+ "输出",
+ "近战位",
+ "近卫干员"
+ ]
+ },
+ "char_337_utage": {
+ "name": "宴",
+ "stars": 4,
+ "tags": [
+ "输出",
+ "生存",
+ "近战位",
+ "近卫干员"
+ ]
+ },
+ "char_271_spikes": {
+ "name": "芳汀",
+ "stars": 4,
+ "tags": [
+ "输出",
"近战位",
- "输出"
+ "近卫干员"
]
},
- "砾": {
+ "char_237_gravel": {
"name": "砾",
"stars": 4,
"tags": [
- "特种干员",
+ "快速复活",
+ "防护",
"近战位",
+ "特种干员"
+ ]
+ },
+ "char_272_strong": {
+ "name": "孑",
+ "stars": 4,
+ "tags": [
"快速复活",
- "防护"
+ "输出",
+ "近战位",
+ "特种干员"
]
},
- "暗索": {
+ "char_236_rope": {
"name": "暗索",
"stars": 4,
"tags": [
- "特种干员",
+ "位移",
"近战位",
- "位移"
+ "特种干员"
]
},
- "末药": {
+ "char_117_myrrh": {
"name": "末药",
"stars": 4,
"tags": [
- "医疗干员",
+ "治疗",
+ "远程位",
+ "医疗干员"
+ ]
+ },
+ "char_298_susuro": {
+ "name": "苏苏洛",
+ "stars": 4,
+ "tags": [
+ "治疗",
"远程位",
- "治疗"
+ "医疗干员"
]
},
- "调香师": {
+ "char_181_flower": {
"name": "调香师",
"stars": 4,
"tags": [
- "医疗干员",
+ "治疗",
+ "远程位",
+ "医疗干员"
+ ]
+ },
+ "char_385_finlpp": {
+ "name": "清流",
+ "stars": 4,
+ "tags": [
+ "治疗",
+ "支援",
"远程位",
- "治疗"
+ "医疗干员"
]
},
- "角峰": {
+ "char_199_yak": {
"name": "角峰",
"stars": 4,
"tags": [
- "重装干员",
+ "防护",
"近战位",
- "防护"
+ "重装干员"
]
},
- "蛇屠箱": {
+ "char_150_snakek": {
"name": "蛇屠箱",
"stars": 4,
"tags": [
- "重装干员",
+ "防护",
"近战位",
- "防护"
+ "重装干员"
]
},
- "古米": {
- "name": "古米",
+ "char_381_bubble": {
+ "name": "泡泡",
"stars": 4,
"tags": [
- "重装干员",
+ "防护",
"近战位",
+ "重装干员"
+ ]
+ },
+ "char_196_sunbr": {
+ "name": "古米",
+ "stars": 4,
+ "tags": [
"防护",
- "治疗"
+ "治疗",
+ "近战位",
+ "重装干员"
]
},
- "地灵": {
+ "char_183_skgoat": {
"name": "地灵",
"stars": 4,
"tags": [
- "辅助干员",
+ "减速",
+ "远程位",
+ "辅助干员"
+ ]
+ },
+ "char_258_podego": {
+ "name": "波登可",
+ "stars": 4,
+ "tags": [
+ "减速",
+ "治疗",
"远程位",
- "减速"
+ "辅助干员"
]
},
- "阿消": {
+ "char_277_sqrrel": {
"name": "阿消",
"stars": 4,
"tags": [
- "特种干员",
+ "位移",
"近战位",
- "位移"
+ "特种干员"
]
},
- "白面鸮": {
+ "char_128_plosis": {
"name": "白面鸮",
"stars": 5,
"tags": [
- "医疗干员",
- "远程位",
"治疗",
- "支援"
+ "支援",
+ "资深干员",
+ "远程位",
+ "医疗干员"
]
},
- "凛冬": {
+ "char_115_headbr": {
"name": "凛冬",
"stars": 5,
"tags": [
- "先锋干员",
- "近战位",
"费用回复",
- "支援"
+ "支援",
+ "资深干员",
+ "近战位",
+ "先锋干员"
]
},
- "德克萨斯": {
+ "char_102_texas": {
"name": "德克萨斯",
"stars": 5,
"tags": [
- "先锋干员",
- "近战位",
"费用回复",
- "控场"
+ "控场",
+ "资深干员",
+ "近战位",
+ "先锋干员"
]
},
- "因陀罗": {
- "name": "因陀罗",
+ "char_349_chiave": {
+ "name": "贾维",
"stars": 5,
"tags": [
- "近卫干员",
- "近战位",
+ "费用回复",
"输出",
- "生存"
+ "资深干员",
+ "近战位",
+ "先锋干员"
]
},
- "幽灵鲨": {
- "name": "幽灵鲨",
+ "char_261_sddrag": {
+ "name": "苇草",
"stars": 5,
"tags": [
- "近卫干员",
+ "费用回复",
+ "输出",
+ "资深干员",
"近战位",
- "群攻",
- "生存"
+ "先锋干员"
]
},
- "蓝毒": {
- "name": "蓝毒",
+ "char_401_elysm": {
+ "name": "极境",
"stars": 5,
"tags": [
- "狙击干员",
- "远程位",
- "输出"
+ "费用回复",
+ "支援",
+ "资深干员",
+ "近战位",
+ "先锋干员"
]
},
- "白金": {
- "name": "白金",
+ "char_308_swire": {
+ "name": "诗怀雅",
"stars": 5,
"tags": [
- "狙击干员",
- "远程位",
- "输出"
+ "输出",
+ "支援",
+ "资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "陨星": {
- "name": "陨星",
+ "char_155_tiger": {
+ "name": "因陀罗",
"stars": 5,
"tags": [
- "狙击干员",
- "远程位",
- "群攻",
- "削弱"
+ "输出",
+ "生存",
+ "资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "梅尔": {
- "name": "梅尔",
+ "char_415_flint": {
+ "name": "燧石",
"stars": 5,
"tags": [
- "辅助干员",
- "远程位",
- "召唤",
- "控场"
+ "输出",
+ "资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "赫默": {
- "name": "赫默",
+ "char_294_ayer": {
+ "name": "断崖",
"stars": 5,
"tags": [
- "医疗干员",
- "远程位",
- "治疗"
+ "输出",
+ "群攻",
+ "资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "华法琳": {
- "name": "华法琳",
+ "char_143_ghost": {
+ "name": "幽灵鲨",
"stars": 5,
"tags": [
- "医疗干员",
- "远程位",
- "治疗",
- "支援"
+ "群攻",
+ "生存",
+ "资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "临光": {
- "name": "临光",
+ "char_356_broca": {
+ "name": "布洛卡",
"stars": 5,
"tags": [
- "重装干员",
+ "群攻",
+ "生存",
+ "资深干员",
"近战位",
- "防护",
- "治疗"
+ "近卫干员"
]
},
- "红": {
- "name": "红",
+ "char_274_astesi": {
+ "name": "星极",
"stars": 5,
"tags": [
- "特种干员",
+ "输出",
+ "防护",
+ "资深干员",
"近战位",
- "快速复活",
- "控场"
+ "近卫干员"
]
},
- "雷蛇": {
- "name": "雷蛇",
+ "char_129_bluep": {
+ "name": "蓝毒",
"stars": 5,
"tags": [
- "重装干员",
- "近战位",
- "防护",
- "输出"
+ "输出",
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "可颂": {
- "name": "可颂",
+ "char_204_platnm": {
+ "name": "白金",
"stars": 5,
"tags": [
- "重装干员",
- "近战位",
- "防护",
- "位移"
+ "输出",
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "火神": {
- "name": "火神",
+ "char_367_swllow": {
+ "name": "灰喉",
"stars": 5,
"tags": [
- "重装干员",
- "近战位",
- "生存",
- "防护",
- "输出"
+ "输出",
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "普罗旺斯": {
- "name": "普罗旺斯",
+ "char_365_aprl": {
+ "name": "四月",
"stars": 5,
"tags": [
- "狙击干员",
+ "输出",
+ "资深干员",
"远程位",
- "输出"
+ "狙击干员"
]
},
- "守林人": {
- "name": "守林人",
+ "char_219_meteo": {
+ "name": "陨星",
"stars": 5,
"tags": [
- "狙击干员",
+ "群攻",
+ "削弱",
+ "资深干员",
"远程位",
- "输出",
- "爆发"
+ "狙击干员"
]
},
- "崖心": {
- "name": "崖心",
+ "char_379_sesa": {
+ "name": "慑砂",
"stars": 5,
"tags": [
- "特种干员",
- "近战位",
- "位移",
- "输出"
+ "群攻",
+ "削弱",
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "初雪": {
- "name": "初雪",
+ "char_279_excu": {
+ "name": "送葬人",
"stars": 5,
"tags": [
- "辅助干员",
+ "群攻",
+ "资深干员",
"远程位",
- "削弱"
+ "狙击干员"
]
},
- "真理": {
- "name": "真理",
+ "char_346_aosta": {
+ "name": "奥斯塔",
"stars": 5,
"tags": [
- "辅助干员",
+ "群攻",
+ "资深干员",
"远程位",
- "减速",
- "输出"
+ "狙击干员"
]
},
- "狮蝎": {
- "name": "狮蝎",
+ "char_306_leizi": {
+ "name": "惊蛰",
"stars": 5,
"tags": [
- "特种干员",
- "近战位",
"输出",
- "生存"
+ "资深干员",
+ "远程位",
+ "术师干员"
]
},
- "食铁兽": {
- "name": "食铁兽",
+ "char_344_beewax": {
+ "name": "蜜蜡",
"stars": 5,
"tags": [
- "特种干员",
- "近战位",
- "位移",
- "减速"
+ "群攻",
+ "防护",
+ "资深干员",
+ "远程位",
+ "术师干员"
]
},
- "能天使": {
- "name": "能天使",
- "stars": 6,
+ "char_373_lionhd": {
+ "name": "莱恩哈特",
+ "stars": 5,
"tags": [
- "狙击干员",
+ "群攻",
+ "爆发",
+ "资深干员",
"远程位",
- "输出"
+ "术师干员"
]
},
- "推进之王": {
- "name": "推进之王",
- "stars": 6,
+ "char_242_otter": {
+ "name": "梅尔",
+ "stars": 5,
"tags": [
- "先锋干员",
- "近战位",
- "费用回复",
- "输出"
+ "召唤",
+ "控场",
+ "资深干员",
+ "远程位",
+ "辅助干员"
]
},
- "伊芙利特": {
- "name": "伊芙利特",
- "stars": 6,
+ "char_108_silent": {
+ "name": "赫默",
+ "stars": 5,
"tags": [
- "术师干员",
+ "治疗",
+ "资深干员",
"远程位",
- "群攻",
- "削弱"
+ "医疗干员"
]
},
- "闪灵": {
- "name": "闪灵",
- "stars": 6,
+ "char_171_bldsk": {
+ "name": "华法琳",
+ "stars": 5,
"tags": [
- "医疗干员",
- "远程位",
"治疗",
- "支援"
+ "支援",
+ "资深干员",
+ "远程位",
+ "医疗干员"
]
},
- "夜莺": {
- "name": "夜莺",
- "stars": 6,
+ "char_436_whispr": {
+ "name": "絮雨",
+ "stars": 5,
"tags": [
- "医疗干员",
- "远程位",
"治疗",
- "支援"
+ "资深干员",
+ "远程位",
+ "医疗干员"
]
},
- "星熊": {
- "name": "星熊",
- "stars": 6,
+ "char_148_nearl": {
+ "name": "临光",
+ "stars": 5,
"tags": [
- "重装干员",
- "近战位",
"防护",
- "输出"
+ "治疗",
+ "资深干员",
+ "近战位",
+ "重装干员"
]
},
- "塞雷娅": {
- "name": "塞雷娅",
- "stars": 6,
+ "char_226_hmau": {
+ "name": "吽",
+ "stars": 5,
"tags": [
- "重装干员",
- "近战位",
"防护",
"治疗",
- "支援"
+ "资深干员",
+ "近战位",
+ "重装干员"
]
},
- "银灰": {
- "name": "银灰",
- "stars": 6,
+ "char_144_red": {
+ "name": "红",
+ "stars": 5,
"tags": [
- "近卫干员",
+ "快速复活",
+ "控场",
+ "资深干员",
"近战位",
- "输出",
- "支援"
+ "特种干员"
]
},
- "空爆": {
- "name": "空爆",
- "stars": 3,
+ "char_243_waaifu": {
+ "name": "槐琥",
+ "stars": 5,
"tags": [
- "狙击干员",
- "远程位",
- "群攻"
+ "快速复活",
+ "削弱",
+ "资深干员",
+ "近战位",
+ "特种干员"
]
},
- "月见夜": {
- "name": "月见夜",
- "stars": 3,
+ "char_107_liskam": {
+ "name": "雷蛇",
+ "stars": 5,
"tags": [
- "近卫干员",
+ "防护",
+ "输出",
+ "资深干员",
"近战位",
- "输出"
+ "重装干员"
]
},
- "猎蜂": {
- "name": "猎蜂",
- "stars": 4,
+ "char_201_moeshd": {
+ "name": "可颂",
+ "stars": 5,
"tags": [
- "近卫干员",
+ "防护",
+ "位移",
+ "资深干员",
"近战位",
- "输出"
+ "重装干员"
]
},
- "夜魔": {
- "name": "夜魔",
+ "char_163_hpsts": {
+ "name": "火神",
"stars": 5,
"tags": [
- "术师干员",
- "远程位",
+ "生存",
+ "防护",
"输出",
- "治疗",
- "减速"
+ "资深干员",
+ "近战位",
+ "重装干员"
]
},
- "斯卡蒂": {
- "name": "斯卡蒂",
- "stars": 6,
+ "char_378_asbest": {
+ "name": "石棉",
+ "stars": 5,
"tags": [
- "近卫干员",
- "近战位",
+ "防护",
"输出",
- "生存"
+ "资深干员",
+ "近战位",
+ "重装干员"
]
},
- "陈": {
- "name": "陈",
- "stars": 6,
+ "char_145_prove": {
+ "name": "普罗旺斯",
+ "stars": 5,
"tags": [
- "近卫干员",
- "近战位",
"输出",
- "爆发"
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "诗怀雅": {
- "name": "诗怀雅",
+ "char_158_milu": {
+ "name": "守林人",
"stars": 5,
"tags": [
- "近卫干员",
- "近战位",
"输出",
- "支援"
+ "爆发",
+ "资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "格雷伊": {
- "name": "格雷伊",
- "stars": 4,
+ "char_218_cuttle": {
+ "name": "安哲拉",
+ "stars": 5,
"tags": [
- "术师干员",
+ "输出",
+ "减速",
+ "资深干员",
"远程位",
- "群攻",
- "减速"
+ "狙击干员"
]
},
- "泡普卡": {
- "name": "泡普卡",
- "stars": 3,
+ "char_173_slchan": {
+ "name": "崖心",
+ "stars": 5,
"tags": [
- "近卫干员",
+ "位移",
+ "输出",
+ "资深干员",
"近战位",
- "群攻",
- "生存"
+ "特种干员"
]
},
- "斑点": {
- "name": "斑点",
- "stars": 3,
+ "char_174_slbell": {
+ "name": "初雪",
+ "stars": 5,
"tags": [
- "重装干员",
- "近战位",
- "防护",
- "治疗"
+ "削弱",
+ "资深干员",
+ "远程位",
+ "辅助干员"
]
},
- "THRM-EX": {
- "name": "THRM-EX",
- "stars": 1,
+ "char_254_vodfox": {
+ "name": "巫恋",
+ "stars": 5,
"tags": [
- "特种干员",
- "近战位",
- "爆发",
- "支援机械"
+ "削弱",
+ "资深干员",
+ "远程位",
+ "辅助干员"
]
},
- "黑": {
- "name": "黑",
- "stars": 6,
+ "char_195_glassb": {
+ "name": "真理",
+ "stars": 5,
"tags": [
- "狙击干员",
+ "减速",
+ "输出",
+ "资深干员",
"远程位",
- "输出"
+ "辅助干员"
]
},
- "赫拉格": {
- "name": "赫拉格",
- "stars": 6,
+ "char_326_glacus": {
+ "name": "格劳克斯",
+ "stars": 5,
"tags": [
- "近卫干员",
- "近战位",
- "输出",
- "生存"
+ "减速",
+ "控场",
+ "资深干员",
+ "远程位",
+ "辅助干员"
]
},
- "格劳克斯": {
- "name": "格劳克斯",
+ "char_343_tknogi": {
+ "name": "月禾",
"stars": 5,
"tags": [
- "辅助干员",
+ "支援",
+ "生存",
+ "资深干员",
"远程位",
- "减速",
- "控场"
+ "辅助干员"
]
},
- "星极": {
- "name": "星极",
+ "char_215_mantic": {
+ "name": "狮蝎",
+ "stars": 5,
+ "tags": [
+ "输出",
+ "生存",
+ "资深干员",
+ "近战位",
+ "特种干员"
+ ]
+ },
+ "char_241_panda": {
+ "name": "食铁兽",
"stars": 5,
"tags": [
- "近卫干员",
+ "位移",
+ "减速",
+ "资深干员",
"近战位",
+ "特种干员"
+ ]
+ },
+ "char_103_angel": {
+ "name": "能天使",
+ "stars": 6,
+ "tags": [
"输出",
- "防护"
+ "高级资深干员",
+ "远程位",
+ "狙击干员"
]
},
- "苏苏洛": {
- "name": "苏苏洛",
- "stars": 4,
+ "char_340_shwaz": {
+ "name": "黑",
+ "stars": 6,
"tags": [
- "医疗干员",
+ "输出",
+ "高级资深干员",
"远程位",
- "治疗"
+ "狙击干员"
]
},
- "桃金娘": {
- "name": "桃金娘",
- "stars": 4,
+ "char_197_poca": {
+ "name": "早露",
+ "stars": 6,
+ "tags": [
+ "输出",
+ "控场",
+ "高级资深干员",
+ "远程位",
+ "狙击干员"
+ ]
+ },
+ "char_112_siege": {
+ "name": "推进之王",
+ "stars": 6,
"tags": [
- "先锋干员",
+ "费用回复",
+ "输出",
+ "高级资深干员",
"近战位",
+ "先锋干员"
+ ]
+ },
+ "char_222_bpipe": {
+ "name": "风笛",
+ "stars": 6,
+ "tags": [
"费用回复",
- "治疗"
+ "输出",
+ "高级资深干员",
+ "近战位",
+ "先锋干员"
]
},
- "麦哲伦": {
- "name": "麦哲伦",
+ "char_134_ifrit": {
+ "name": "伊芙利特",
"stars": 6,
"tags": [
- "辅助干员",
+ "群攻",
+ "削弱",
+ "高级资深干员",
"远程位",
- "支援",
- "减速",
- "输出"
+ "术师干员"
]
},
- "送葬人": {
- "name": "送葬人",
- "stars": 5,
+ "char_213_mostma": {
+ "name": "莫斯提马",
+ "stars": 6,
"tags": [
- "狙击干员",
+ "群攻",
+ "支援",
+ "控场",
+ "高级资深干员",
"远程位",
- "群攻"
+ "术师干员"
]
},
- "红云": {
- "name": "红云",
- "stars": 4,
+ "char_2013_cerber": {
+ "name": "刻俄柏",
+ "stars": 6,
"tags": [
- "狙击干员",
+ "输出",
+ "控场",
+ "高级资深干员",
"远程位",
- "输出"
+ "术师干员"
]
},
- "莫斯提马": {
- "name": "莫斯提马",
+ "char_358_lisa": {
+ "name": "铃兰",
"stars": 6,
"tags": [
- "术师干员",
+ "减速",
+ "支援",
+ "输出",
+ "高级资深干员",
"远程位",
- "群攻",
+ "辅助干员"
+ ]
+ },
+ "char_248_mgllan": {
+ "name": "麦哲伦",
+ "stars": 6,
+ "tags": [
"支援",
- "控场"
+ "减速",
+ "输出",
+ "高级资深干员",
+ "远程位",
+ "辅助干员"
]
},
- "槐琥": {
- "name": "槐琥",
- "stars": 5,
+ "char_250_phatom": {
+ "name": "傀影",
+ "stars": 6,
"tags": [
- "特种干员",
- "近战位",
"快速复活",
- "削弱"
+ "控场",
+ "输出",
+ "高级资深干员",
+ "近战位",
+ "特种干员"
]
},
- "清流": {
- "name": "清流",
- "stars": 4,
+ "char_400_weedy": {
+ "name": "温蒂",
+ "stars": 6,
+ "tags": [
+ "位移",
+ "输出",
+ "控场",
+ "高级资深干员",
+ "近战位",
+ "特种干员"
+ ]
+ },
+ "char_225_haak": {
+ "name": "阿",
+ "stars": 6,
"tags": [
- "医疗干员",
+ "支援",
+ "输出",
+ "高级资深干员",
"远程位",
+ "特种干员"
+ ]
+ },
+ "char_147_shining": {
+ "name": "闪灵",
+ "stars": 6,
+ "tags": [
"治疗",
- "支援"
+ "支援",
+ "高级资深干员",
+ "远程位",
+ "医疗干员"
]
},
- "梅": {
- "name": "梅",
- "stars": 4,
+ "char_179_cgbird": {
+ "name": "夜莺",
+ "stars": 6,
"tags": [
- "狙击干员",
+ "治疗",
+ "支援",
+ "高级资深干员",
"远程位",
- "输出",
- "减速"
+ "医疗干员"
]
},
- "煌": {
- "name": "煌",
+ "char_136_hsguma": {
+ "name": "星熊",
"stars": 6,
"tags": [
- "近卫干员",
- "近战位",
+ "防护",
"输出",
- "生存"
+ "高级资深干员",
+ "近战位",
+ "重装干员"
]
},
- "灰喉": {
- "name": "灰喉",
- "stars": 5,
+ "char_202_demkni": {
+ "name": "塞雷娅",
+ "stars": 6,
"tags": [
- "狙击干员",
- "远程位",
- "输出"
+ "防护",
+ "治疗",
+ "支援",
+ "高级资深干员",
+ "近战位",
+ "重装干员"
]
},
- "苇草": {
- "name": "苇草",
- "stars": 5,
+ "char_423_blemsh": {
+ "name": "瑕光",
+ "stars": 6,
"tags": [
- "先锋干员",
+ "防护",
+ "治疗",
+ "输出",
+ "高级资深干员",
"近战位",
- "费用回复",
- "输出"
+ "重装干员"
]
},
- "布洛卡": {
- "name": "布洛卡",
- "stars": 5,
+ "char_311_mudrok": {
+ "name": "泥岩",
+ "stars": 6,
"tags": [
- "近卫干员",
+ "生存",
+ "防护",
+ "输出",
+ "高级资深干员",
"近战位",
- "群攻",
- "生存"
+ "重装干员"
]
},
- "安比尔": {
- "name": "安比尔",
- "stars": 4,
+ "char_416_zumama": {
+ "name": "森蚺",
+ "stars": 6,
"tags": [
- "狙击干员",
- "远程位",
"输出",
- "减速"
+ "生存",
+ "防护",
+ "高级资深干员",
+ "近战位",
+ "重装干员"
]
},
- "阿": {
- "name": "阿",
+ "char_172_svrash": {
+ "name": "银灰",
"stars": 6,
"tags": [
- "特种干员",
- "远程位",
"输出",
- "支援"
+ "支援",
+ "高级资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "吽": {
- "name": "吽",
- "stars": 5,
+ "char_293_thorns": {
+ "name": "棘刺",
+ "stars": 6,
"tags": [
- "重装干员",
- "近战位",
+ "输出",
"防护",
- "治疗"
+ "高级资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "正义骑士号": {
- "name": "正义骑士号",
- "stars": 1,
+ "char_010_chen": {
+ "name": "陈",
+ "stars": 6,
"tags": [
- "狙击干员",
- "远程位",
- "支援",
- "支援机械"
+ "爆发",
+ "输出",
+ "高级资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "刻俄柏": {
- "name": "刻俄柏",
+ "char_017_huang": {
+ "name": "煌",
"stars": 6,
"tags": [
- "术师干员",
- "远程位",
"输出",
- "控场"
+ "生存",
+ "高级资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "惊蛰": {
- "name": "惊蛰",
- "stars": 5,
+ "char_350_surtr": {
+ "name": "史尔特尔",
+ "stars": 6,
"tags": [
- "术师干员",
- "远程位",
- "输出"
+ "输出",
+ "高级资深干员",
+ "近战位",
+ "近卫干员"
]
},
- "风笛": {
- "name": "风笛",
+ "char_188_helage": {
+ "name": "赫拉格",
"stars": 6,
"tags": [
- "先锋干员",
+ "输出",
+ "生存",
+ "高级资深干员",
"近战位",
- "费用回复",
- "输出"
+ "近卫干员"
]
},
- "慑砂": {
- "name": "慑砂",
- "stars": 5,
+ "char_282_catap": {
+ "name": "空爆",
+ "stars": 3,
"tags": [
- "狙击干员",
- "远程位",
"群攻",
- "削弱"
+ "远程位",
+ "狙击干员"
]
},
- "宴": {
- "name": "宴",
- "stars": 4,
+ "char_283_midn": {
+ "name": "月见夜",
+ "stars": 3,
"tags": [
- "近卫干员",
+ "输出",
"近战位",
+ "近卫干员"
+ ]
+ },
+ "char_137_brownb": {
+ "name": "猎蜂",
+ "stars": 4,
+ "tags": [
"输出",
- "生存"
+ "近战位",
+ "近卫干员"
]
},
- "傀影": {
- "name": "傀影",
- "stars": 6,
+ "char_347_jaksel": {
+ "name": "杰克",
+ "stars": 4,
"tags": [
- "特种干员",
+ "输出",
"近战位",
- "快速复活",
- "控场",
- "输出"
+ "近卫干员"
]
},
- "巫恋": {
- "name": "巫恋",
+ "char_164_nightm": {
+ "name": "夜魔",
"stars": 5,
"tags": [
- "辅助干员",
+ "输出",
+ "治疗",
+ "减速",
+ "资深干员",
"远程位",
- "削弱"
+ "术师干员"
]
},
- "刻刀": {
- "name": "刻刀",
- "stars": 4,
+ "char_263_skadi": {
+ "name": "斯卡蒂",
+ "stars": 6,
"tags": [
- "近卫干员",
+ "输出",
+ "生存",
+ "高级资深干员",
"近战位",
- "爆发",
- "输出"
+ "近卫干员"
]
}
}
\ No newline at end of file
diff --git a/arknights_mower/data/recruit_result.json b/arknights_mower/data/recruit_result.json
new file mode 100644
index 000000000..8446f968f
--- /dev/null
+++ b/arknights_mower/data/recruit_result.json
@@ -0,0 +1,152 @@
+{
+ "4": [
+ "char_211_adnach",
+ "char_210_stward",
+ "char_127_estell",
+ "char_102_texas",
+ "char_373_lionhd",
+ "char_145_prove",
+ "char_326_glacus",
+ "char_112_siege",
+ "char_134_ifrit",
+ "char_213_mostma",
+ "char_350_surtr"
+ ],
+ "3": [
+ "char_503_rang",
+ "char_009_12fce",
+ "char_208_melan",
+ "char_281_popka",
+ "char_122_beagle",
+ "char_124_kroos",
+ "char_212_ansel",
+ "char_253_greyy",
+ "char_235_jesica",
+ "char_302_glaze",
+ "char_149_scave",
+ "char_151_myrtle",
+ "char_298_susuro",
+ "char_181_flower",
+ "char_150_snakek",
+ "char_258_podego",
+ "char_128_plosis",
+ "char_308_swire",
+ "char_155_tiger",
+ "char_143_ghost",
+ "char_356_broca",
+ "char_279_excu",
+ "char_346_aosta",
+ "char_171_bldsk",
+ "char_158_milu",
+ "char_218_cuttle",
+ "char_241_panda",
+ "char_103_angel",
+ "char_2013_cerber",
+ "char_248_mgllan",
+ "char_202_demkni",
+ "char_188_helage",
+ "char_283_midn",
+ "char_263_skadi"
+ ],
+ "2": [
+ "char_502_nblade",
+ "char_500_noirc",
+ "char_501_durin",
+ "char_240_wyvern",
+ "char_192_falco",
+ "char_284_spot",
+ "char_121_lava",
+ "char_120_hibisc",
+ "char_278_orchid",
+ "char_141_nights",
+ "char_109_fmout",
+ "char_328_cammou",
+ "char_126_shotst",
+ "char_190_clour",
+ "char_118_yuki",
+ "char_366_acdrop",
+ "char_290_vigna",
+ "char_130_doberm",
+ "char_289_gyuki",
+ "char_193_frostl",
+ "char_185_frncat",
+ "char_301_cutter",
+ "char_271_spikes",
+ "char_236_rope",
+ "char_117_myrrh",
+ "char_385_finlpp",
+ "char_199_yak",
+ "char_381_bubble",
+ "char_196_sunbr",
+ "char_183_skgoat",
+ "char_277_sqrrel",
+ "char_115_headbr",
+ "char_349_chiave",
+ "char_261_sddrag",
+ "char_401_elysm",
+ "char_415_flint",
+ "char_294_ayer",
+ "char_274_astesi",
+ "char_129_bluep",
+ "char_204_platnm",
+ "char_367_swllow",
+ "char_365_aprl",
+ "char_219_meteo",
+ "char_379_sesa",
+ "char_306_leizi",
+ "char_344_beewax",
+ "char_242_otter",
+ "char_108_silent",
+ "char_436_whispr",
+ "char_148_nearl",
+ "char_243_waaifu",
+ "char_107_liskam",
+ "char_201_moeshd",
+ "char_163_hpsts",
+ "char_378_asbest",
+ "char_173_slchan",
+ "char_174_slbell",
+ "char_254_vodfox",
+ "char_195_glassb",
+ "char_343_tknogi",
+ "char_215_mantic",
+ "char_197_poca",
+ "char_222_bpipe",
+ "char_358_lisa",
+ "char_250_phatom",
+ "char_400_weedy",
+ "char_147_shining",
+ "char_179_cgbird",
+ "char_136_hsguma",
+ "char_423_blemsh",
+ "char_311_mudrok",
+ "char_416_zumama",
+ "char_172_svrash",
+ "char_293_thorns",
+ "char_282_catap",
+ "char_137_brownb",
+ "char_347_jaksel",
+ "char_164_nightm"
+ ],
+ "1": [
+ "char_123_fang",
+ "char_133_mm",
+ "char_337_utage",
+ "char_237_gravel",
+ "char_272_strong",
+ "char_226_hmau",
+ "char_144_red",
+ "char_340_shwaz",
+ "char_225_haak",
+ "char_010_chen",
+ "char_017_huang"
+ ],
+ "-1": [
+ "char_285_medic2",
+ "char_286_cast3",
+ "char_376_therex",
+ "char_4000_jnight",
+ "char_4093_frston",
+ "char_4136_phonor"
+ ]
+}
\ No newline at end of file
diff --git a/arknights_mower/data/scene.json b/arknights_mower/data/scene.json
index 9dd47a732..05ae39015 100644
--- a/arknights_mower/data/scene.json
+++ b/arknights_mower/data/scene.json
@@ -37,7 +37,7 @@
},
"8": {
"label": "DOUBLE_CONFIRM",
- "comment": "二次确认"
+ "comment": "二次确认(未知)"
},
"9": {
"label": "CONNECTING",
@@ -47,6 +47,18 @@
"label": "NETWORK_CHECK",
"comment": "网络拨测"
},
+ "11": {
+ "label": "EXIT_GAME",
+ "comment": "退出游戏"
+ },
+ "12": {
+ "label": "DOWNLOAD_VOICE_RESOURCES",
+ "comment": "检测到有未下载的语音资源"
+ },
+ "13": {
+ "label": "AGREEMENT_UPDATE",
+ "comment": "协议更新"
+ },
"101": {
"label": "LOGIN_MAIN",
"comment": "登录页面"
@@ -95,6 +107,18 @@
"label": "CLOSE_MINE",
"comment": "产业合作洽谈会"
},
+ "113": {
+ "label": "CHECK_IN",
+ "comment": "4周年签到"
+ },
+ "114": {
+ "label": "LOGIN_NEW",
+ "comment": "新登陆界面"
+ },
+ "116": {
+ "label": "LOGIN_BILIBILI_PRIVACY",
+ "comment": "B服隐私政策提示"
+ },
"201": {
"label": "INFRA_MAIN",
"comment": "基建全局视角"
@@ -123,18 +147,90 @@
"label": "INFRA_ARRANGE_ORDER",
"comment": "干员进驻设施排序界面"
},
+ "208": {
+ "label": "RIIC_REPORT",
+ "comment": "副手简报界面"
+ },
+ "209": {
+ "label": "CTRLCENTER_ASSISTANT",
+ "comment": "控制中枢界面"
+ },
+ "210": {
+ "label": "RIIC_OPERATOR_SELECT",
+ "comment": "干员选择界面"
+ },
+ "211": {
+ "label": "CLUE_DAILY",
+ "comment": "每日线索领取"
+ },
+ "212": {
+ "label": "CLUE_RECEIVE",
+ "comment": "接收线索"
+ },
+ "213": {
+ "label": "CLUE_GIVE_AWAY",
+ "comment": "传递线索"
+ },
+ "214": {
+ "label": "CLUE_SUMMARY",
+ "comment": "线索交流活动汇总"
+ },
+ "215": {
+ "label": "CLUE_PLACE",
+ "comment": "放置线索"
+ },
+ "216": {
+ "label": "TRAIN_SKILL_UPGRADE",
+ "comment": "技能升级"
+ },
+ "217": {
+ "label": "TRAIN_MAIN",
+ "comment": "训练室主界面"
+ },
+ "218": {
+ "label": "TRAIN_SKILL_UPGRADE_ERROR",
+ "comment": "技能升级失败"
+ },
+ "219": {
+ "label": "TRAIN_SKILL_SELECT",
+ "comment": "选择技能"
+ },
+ "220": {
+ "label": "TRAIN_FINISH",
+ "comment": "技能升级结算"
+ },
+ "221": {
+ "label": "ORDER_LIST",
+ "comment": "贸易站订单列表"
+ },
+ "222": {
+ "label": "DRONE_ACCELERATE",
+ "comment": "无人机加速对话框"
+ },
+ "223": {
+ "label": "FACTORY_ROOMS",
+ "comment": "制造站设施列表"
+ },
+ "224": {
+ "label": "LEAVE_INFRASTRUCTURE",
+ "comment": "离开基建"
+ },
"301": {
- "label": "FRIEND_LIST_OFF",
- "comment": "好友列表(未选中)"
+ "label": "BUSINESS_CARD",
+ "comment": "个人名片"
},
"302": {
- "label": "FRIEND_LIST_ON",
- "comment": "好友列表(选中)"
+ "label": "FRIEND_LIST",
+ "comment": "好友列表"
},
"303": {
"label": "FRIEND_VISITING",
"comment": "基建内访问好友"
},
+ "304": {
+ "label": "BACK_TO_FRIEND_LIST",
+ "comment": "返回好友列表"
+ },
"401": {
"label": "MISSION_DAILY",
"comment": "日常任务"
@@ -167,6 +263,22 @@
"label": "TERMINAL_COLLECTION",
"comment": "资源收集"
},
+ "506": {
+ "label": "TERMINAL_REGULAR",
+ "comment": "常态事务"
+ },
+ "507": {
+ "label": "TERMINAL_LONGTERM",
+ "comment": "长期探索"
+ },
+ "508": {
+ "label": "TERMINAL_PERIODIC",
+ "comment": "周期挑战"
+ },
+ "601": {
+ "label": "OPERATOR_CHOOSE_LEVEL",
+ "comment": "作战前,关卡未选定"
+ },
"602": {
"label": "OPERATOR_BEFORE",
"comment": "作战前,关卡已选定"
@@ -177,7 +289,7 @@
},
"604": {
"label": "OPERATOR_ONGOING",
- "comment": "作战中"
+ "comment": "代理作战"
},
"605": {
"label": "OPERATOR_FINISH",
@@ -205,7 +317,7 @@
},
"612": {
"label": "OPERATOR_GIVEUP",
- "comment": "代理失误"
+ "comment": "放弃行动"
},
"613": {
"label": "OPERATOR_FAILED",
@@ -215,6 +327,14 @@
"label": "OPERATOR_ELIMINATE_AGENCY",
"comment": "剿灭代理卡使用确认"
},
+ "615": {
+ "label": "OPERATOR_SUPPORT",
+ "comment": "借助战"
+ },
+ "616": {
+ "label": "OPERATOR_STRANGER_SUPPORT",
+ "comment": "使用非好友助战"
+ },
"701": {
"label": "SHOP_OTHERS",
"comment": "商店除了信用兑换处以外的界面"
@@ -231,6 +351,10 @@
"label": "SHOP_ASSIST",
"comment": "助战使用次数"
},
+ "705": {
+ "label": "SHOP_UNLOCK_SCHEDULE",
+ "comment": "累计信用消费"
+ },
"801": {
"label": "RECRUIT_MAIN",
"comment": "公招主界面"
@@ -243,6 +367,190 @@
"label": "RECRUIT_AGENT",
"comment": "开包干员展示"
},
+ "804": {
+ "label": "REFRESH_TAGS",
+ "comment": "刷新词条"
+ },
+ "901": {
+ "label": "RA_MAIN",
+ "comment": "生息演算首页"
+ },
+ "902": {
+ "label": "RA_GUIDE_ENTRANCE",
+ "comment": "剧情入口:众人会聚之地(后舍)"
+ },
+ "903": {
+ "label": "RA_GUIDE_DIALOG",
+ "comment": "剧情对话"
+ },
+ "904": {
+ "label": "RA_BATTLE_ENTRANCE",
+ "comment": "作战入口"
+ },
+ "905": {
+ "label": "RA_BATTLE",
+ "comment": "作战中"
+ },
+ "906": {
+ "label": "RA_BATTLE_EXIT_CONFIRM",
+ "comment": "作战退出确认对话框"
+ },
+ "907": {
+ "label": "RA_GUIDE_BATTLE_ENTRANCE",
+ "comment": "剧情作战入口"
+ },
+ "908": {
+ "label": "RA_BATTLE_COMPLETE",
+ "comment": "作战结算"
+ },
+ "909": {
+ "label": "RA_MAP",
+ "comment": "地图"
+ },
+ "910": {
+ "label": "RA_SQUAD_EDIT",
+ "comment": "作战分队编辑"
+ },
+ "911": {
+ "label": "RA_KITCHEN",
+ "comment": "烹饪台"
+ },
+ "912": {
+ "label": "RA_GET_ITEM",
+ "comment": "获得物资"
+ },
+ "913": {
+ "label": "RA_SQUAD_EDIT_DIALOG",
+ "comment": "作战分队不携带干员确认"
+ },
+ "914": {
+ "label": "RA_DAY_COMPLETE",
+ "comment": "生息日结算"
+ },
+ "915": {
+ "label": "RA_DAY_DETAIL",
+ "comment": "当日详细信息"
+ },
+ "916": {
+ "label": "RA_WASTE_TIME_DIALOG",
+ "comment": "跳过行动确认对话框"
+ },
+ "917": {
+ "label": "RA_PERIOD_COMPLETE",
+ "comment": "生存周期完成"
+ },
+ "918": {
+ "label": "RA_DELETE_SAVE_DIALOG",
+ "comment": "存档删除确认"
+ },
+ "919": {
+ "label": "RA_ADVENTURE",
+ "comment": "奇遇"
+ },
+ "920": {
+ "label": "RA_NOTICE",
+ "comment": "一张便条"
+ },
+ "921": {
+ "label": "RA_INSUFFICIENT_DRINK",
+ "comment": "能量饮料不足"
+ },
+ "922": {
+ "label": "RA_SQUAD_ABNORMAL",
+ "comment": "当前编队中存在异常情况"
+ },
+ "1001": {
+ "label": "SSS_MAIN",
+ "comment": "保全作战首页"
+ },
+ "1002": {
+ "label": "SSS_START",
+ "comment": "开始保全作战"
+ },
+ "1003": {
+ "label": "SSS_EC",
+ "comment": "定向导能元件"
+ },
+ "1004": {
+ "label": "SSS_DEVICE",
+ "comment": "战术装备配置"
+ },
+ "1005": {
+ "label": "SSS_SQUAD",
+ "comment": "首批作战小队选任"
+ },
+ "1006": {
+ "label": "SSS_DEPLOY",
+ "comment": "开始部署"
+ },
+ "1007": {
+ "label": "SSS_REDEPLOY",
+ "comment": "重新部署"
+ },
+ "1008": {
+ "label": "SSS_LOADING",
+ "comment": "正在进入"
+ },
+ "1009": {
+ "label": "SSS_GUIDE",
+ "comment": "保全教程"
+ },
+ "1101": {
+ "label": "SF_ENTRANCE",
+ "comment": "隐秘战线入口"
+ },
+ "1102": {
+ "label": "SF_INFO",
+ "comment": "隐秘战线详细说明"
+ },
+ "1103": {
+ "label": "SF_SELECT_TEAM",
+ "comment": "选择小队"
+ },
+ "1104": {
+ "label": "SF_CONTINUE",
+ "comment": "继续前进"
+ },
+ "1105": {
+ "label": "SF_SELECT",
+ "comment": "选择路线"
+ },
+ "1106": {
+ "label": "SF_ACTIONS",
+ "comment": "行动选项"
+ },
+ "1107": {
+ "label": "SF_RESULT",
+ "comment": "行动结果"
+ },
+ "1108": {
+ "label": "SF_EVENT",
+ "comment": "应对危机事件"
+ },
+ "1109": {
+ "label": "SF_TEAM_PASS",
+ "comment": "小队通过危机事件"
+ },
+ "1110": {
+ "label": "SF_CLICK_ANYWHERE",
+ "comment": "点击任意处继续"
+ },
+ "1111": {
+ "label": "SF_END",
+ "comment": "抵达终点"
+ },
+ "1112": {
+ "label": "SF_EXIT",
+ "comment": "暂离行动"
+ },
+ "1201": {
+ "label": "HEADHUNTING",
+ "comment": "干员寻访"
+ },
+ "1301": {
+ "label": "DEPOT",
+ "comment": "仓库"
+ },
"9998": {
"label": "LOADING",
"comment": "场景跳转时的等待界面"
@@ -251,4 +559,4 @@
"label": "CONFIRM",
"comment": "确认对话框"
}
-}
+}
\ No newline at end of file
diff --git a/arknights_mower/data/shop.json b/arknights_mower/data/shop.json
index 0ddd4620c..5a92404d3 100644
--- a/arknights_mower/data/shop.json
+++ b/arknights_mower/data/shop.json
@@ -1,25 +1,25 @@
-[
- "龙门币",
- "基础作战记录",
- "源岩",
- "固源岩",
- "初级作战记录",
- "代糖",
- "糖",
- "家具零件",
- "技巧概要·卷1",
- "酯原料",
- "聚酸酯",
- "技巧概要·卷2",
- "异铁碎片",
- "异铁",
- "招聘许可",
- "碳",
- "双酮",
- "酮凝集",
- "加急许可",
- "碳素",
- "破损装置",
- "装置",
- "赤金"
-]
\ No newline at end of file
+{
+ "招聘许可": "100.00",
+ "龙门币": "36.64",
+ "技巧概要·卷2": "34.32",
+ "初级作战记录": "29.38",
+ "基础作战记录": "29.38",
+ "技巧概要·卷1": "29.36",
+ "异铁": "29.09",
+ "装置": "29.02",
+ "酮凝集": "28.89",
+ "固源岩": "28.69",
+ "糖": "28.46",
+ "聚酸酯": "28.29",
+ "赤金": "24.49",
+ "代糖": "21.73",
+ "异铁碎片": "21.70",
+ "酯原料": "21.62",
+ "双酮": "21.56",
+ "破损装置": "21.07",
+ "源岩": "19.39",
+ "碳": "15.31",
+ "碳素": "11.37",
+ "家具零件": "0.00",
+ "加急许可": "0.00"
+}
\ No newline at end of file
diff --git a/arknights_mower/data/stage_data.json b/arknights_mower/data/stage_data.json
new file mode 100644
index 000000000..ed9ea4eb8
--- /dev/null
+++ b/arknights_mower/data/stage_data.json
@@ -0,0 +1,158 @@
+[
+ {
+ "id": "",
+ "name": "上次作战",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "1-7",
+ "name": "1-7",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "Annihilation",
+ "name": "剿灭",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "LS-6",
+ "name": "经验书",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "CE-6",
+ "name": "龙门币",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "AP-5",
+ "name": "红票",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "SK-6",
+ "name": "碳条",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0
+ },
+ {
+ "id": "CA-5",
+ "name": "技能书",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1
+ },
+ {
+ "id": "PR-A-2",
+ "name": "重装医疗2",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1
+ },
+ {
+ "id": "PR-B-2",
+ "name": "狙击术士2",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 0,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0
+ },
+ {
+ "id": "PR-C-2",
+ "name": "先锋辅助2",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 0,
+ "周三": 1,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1
+ },
+ {
+ "id": "PR-D-2",
+ "name": "近卫特种2",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1
+ }
+]
\ No newline at end of file
diff --git a/arknights_mower/fonts/SourceHanSansCN-Medium.otf b/arknights_mower/fonts/SourceHanSansCN-Medium.otf
new file mode 100644
index 000000000..630d54677
Binary files /dev/null and b/arknights_mower/fonts/SourceHanSansCN-Medium.otf differ
diff --git a/arknights_mower/fonts/SourceHanSansSC-Bold.otf b/arknights_mower/fonts/SourceHanSansSC-Bold.otf
deleted file mode 100644
index 16ecccf9f..000000000
Binary files a/arknights_mower/fonts/SourceHanSansSC-Bold.otf and /dev/null differ
diff --git a/arknights_mower/models/CONSUME.pkl b/arknights_mower/models/CONSUME.pkl
new file mode 100644
index 000000000..e8055130d
Binary files /dev/null and b/arknights_mower/models/CONSUME.pkl differ
diff --git a/arknights_mower/models/MATERIAL.pkl b/arknights_mower/models/MATERIAL.pkl
new file mode 100644
index 000000000..e6f3bd722
Binary files /dev/null and b/arknights_mower/models/MATERIAL.pkl differ
diff --git a/arknights_mower/models/NORMAL.pkl b/arknights_mower/models/NORMAL.pkl
new file mode 100644
index 000000000..cb42f08dc
Binary files /dev/null and b/arknights_mower/models/NORMAL.pkl differ
diff --git a/arknights_mower/models/README.md b/arknights_mower/models/README.md
index 1365f0484..946b88369 100644
--- a/arknights_mower/models/README.md
+++ b/arknights_mower/models/README.md
@@ -1,13 +1,16 @@
-# Models
-
-## dbnet.onnx
-
-DBNET 的模型文件,负责提取图像中的文字
-
-## crnn_lite_lstm.onnx
-
-CRNN 的轻量型模型文件,负责识别图像中的文字
-
-## svm.model
-
-SVM 分类器的模型文件,负责图像匹配判定
+# Models
+
+## dbnet.onnx
+
+DBNET 的模型文件,负责提取图像中的文字
+
+## crnn_lite_lstm.onnx
+
+CRNN 的轻量型模型文件,负责识别图像中的文字
+
+## svm.model
+
+SVM 分类器的模型文件,负责图像匹配判定
+
+## depot.pkl
+仓库物品的knn模型,负责仓库中的物品
\ No newline at end of file
diff --git a/arknights_mower/models/__init__.py b/arknights_mower/models/__init__.py
new file mode 100644
index 000000000..e7548ff19
--- /dev/null
+++ b/arknights_mower/models/__init__.py
@@ -0,0 +1,24 @@
+import lzma
+import pickle
+
+from arknights_mower import __rootdir__
+
+with lzma.open(f"{__rootdir__}/models/avatar.pkl", "rb") as f:
+ avatar = pickle.load(f)
+
+
+with lzma.open(f"{__rootdir__}/models/secret_front.pkl", "rb") as f:
+ secret_front = pickle.load(f)
+
+
+with lzma.open(f"{__rootdir__}/models/navigation.pkl", "rb") as f:
+ navigation = pickle.load(f)
+
+with lzma.open(f"{__rootdir__}/models/riic_base_digits.pkl", "rb") as f:
+ riic_base_digits = pickle.load(f)
+
+with lzma.open(f"{__rootdir__}/models/noto_sans.pkl", "rb") as f:
+ noto_sans = pickle.load(f)
+
+with lzma.open(f"{__rootdir__}/models/shop.pkl", "rb") as f:
+ shop = pickle.load(f)
diff --git a/arknights_mower/models/avatar.pkl b/arknights_mower/models/avatar.pkl
new file mode 100644
index 000000000..744ea1490
Binary files /dev/null and b/arknights_mower/models/avatar.pkl differ
diff --git a/arknights_mower/models/crnn_lite_lstm.onnx b/arknights_mower/models/crnn_lite_lstm.onnx
deleted file mode 100644
index 51a15c3cf..000000000
Binary files a/arknights_mower/models/crnn_lite_lstm.onnx and /dev/null differ
diff --git a/arknights_mower/models/dbnet.onnx b/arknights_mower/models/dbnet.onnx
deleted file mode 100644
index 0b1b16275..000000000
Binary files a/arknights_mower/models/dbnet.onnx and /dev/null differ
diff --git a/arknights_mower/models/levels.pkl b/arknights_mower/models/levels.pkl
new file mode 100644
index 000000000..bd636af28
Binary files /dev/null and b/arknights_mower/models/levels.pkl differ
diff --git a/arknights_mower/models/navigation.pkl b/arknights_mower/models/navigation.pkl
new file mode 100644
index 000000000..8691ec462
Binary files /dev/null and b/arknights_mower/models/navigation.pkl differ
diff --git a/arknights_mower/models/noto_sans.pkl b/arknights_mower/models/noto_sans.pkl
new file mode 100644
index 000000000..af851cc21
Binary files /dev/null and b/arknights_mower/models/noto_sans.pkl differ
diff --git a/arknights_mower/models/operator_room.model b/arknights_mower/models/operator_room.model
new file mode 100644
index 000000000..5aa9be85e
Binary files /dev/null and b/arknights_mower/models/operator_room.model differ
diff --git a/arknights_mower/models/operator_select.model b/arknights_mower/models/operator_select.model
new file mode 100644
index 000000000..fe5569554
Binary files /dev/null and b/arknights_mower/models/operator_select.model differ
diff --git a/arknights_mower/models/recruit.pkl b/arknights_mower/models/recruit.pkl
new file mode 100644
index 000000000..53d8bb6ce
Binary files /dev/null and b/arknights_mower/models/recruit.pkl differ
diff --git a/arknights_mower/models/recruit_result.pkl b/arknights_mower/models/recruit_result.pkl
new file mode 100644
index 000000000..e62757f55
Binary files /dev/null and b/arknights_mower/models/recruit_result.pkl differ
diff --git a/arknights_mower/models/riic_base_digits.pkl b/arknights_mower/models/riic_base_digits.pkl
new file mode 100644
index 000000000..977a7f593
Binary files /dev/null and b/arknights_mower/models/riic_base_digits.pkl differ
diff --git a/arknights_mower/models/secret_front.pkl b/arknights_mower/models/secret_front.pkl
new file mode 100644
index 000000000..fcdb5e746
Binary files /dev/null and b/arknights_mower/models/secret_front.pkl differ
diff --git a/arknights_mower/models/shop.pkl b/arknights_mower/models/shop.pkl
new file mode 100644
index 000000000..21c70344e
Binary files /dev/null and b/arknights_mower/models/shop.pkl differ
diff --git a/arknights_mower/models/svm.model b/arknights_mower/models/svm.model
index f4f0b3422..c036f4b62 100644
Binary files a/arknights_mower/models/svm.model and b/arknights_mower/models/svm.model differ
diff --git a/arknights_mower/ocr/__init__.py b/arknights_mower/ocr/__init__.py
deleted file mode 100644
index 197f17b5d..000000000
--- a/arknights_mower/ocr/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .model import OcrHandle
-from .rectify import ocr_rectify
-
-ocrhandle = OcrHandle()
diff --git a/arknights_mower/ocr/config.py b/arknights_mower/ocr/config.py
deleted file mode 100644
index dfae5f340..000000000
--- a/arknights_mower/ocr/config.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .. import __rootdir__
-
-dbnet_model_path = f'{__rootdir__}/models/dbnet.onnx'
-crnn_model_path = f'{__rootdir__}/models/crnn_lite_lstm.onnx'
diff --git a/arknights_mower/ocr/crnn.py b/arknights_mower/ocr/crnn.py
deleted file mode 100644
index 077f58288..000000000
--- a/arknights_mower/ocr/crnn.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import numpy as np
-import onnxruntime as rt
-from PIL import Image
-
-from .keys import alphabetChinese as alphabet
-from .utils import resizeNormalize, strLabelConverter
-
-converter = strLabelConverter(''.join(alphabet))
-
-
-class CRNNHandle:
- def __init__(self, model_path):
- sess_options = rt.SessionOptions()
- sess_options.log_severity_level = 3
- self.sess = rt.InferenceSession(model_path, sess_options)
-
- def predict(self, image):
- scale = image.size[1] * 1.0 / 32
- w = image.size[0] / scale
- w = int(w)
- transformer = resizeNormalize((w, 32))
- image = transformer(image)
- image = image.transpose(2, 0, 1)
- transformed_image = np.expand_dims(image, axis=0)
- transformed_image = np.array([[transformed_image[0, 0]] * 3])
- preds = self.sess.run(
- ['out'], {'input': transformed_image.astype(np.float32)})
- preds = preds[0]
- length = preds.shape[0]
- preds = preds.reshape(length, -1)
- preds = np.argmax(preds, axis=1)
- preds = preds.reshape(-1)
- sim_pred = converter.decode(preds, length, raw=False)
- return sim_pred
-
- def predict_rbg(self, image):
- scale = image.size[1] * 1.0 / 32
- w = image.size[0] / scale
- w = int(w)
- image = image.resize((w, 32), Image.BILINEAR)
- image = np.array(image, dtype=np.float32)
- image -= 127.5
- image /= 127.5
- image = image.transpose(2, 0, 1)
- transformed_image = np.expand_dims(image, axis=0)
- preds = self.sess.run(
- ['out'], {'input': transformed_image.astype(np.float32)})
- preds = preds[0]
- length = preds.shape[0]
- preds = preds.reshape(length, -1)
- preds = np.argmax(preds, axis=1)
- preds = preds.reshape(-1)
- sim_pred = converter.decode(preds, length, raw=False)
- return sim_pred
diff --git a/arknights_mower/ocr/dbnet.py b/arknights_mower/ocr/dbnet.py
deleted file mode 100644
index 227e6f23d..000000000
--- a/arknights_mower/ocr/dbnet.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import cv2
-import numpy as np
-import onnxruntime as rt
-
-from .decode import SegDetectorRepresenter
-
-mean = (0.485, 0.456, 0.406)
-std = (0.229, 0.224, 0.225)
-
-
-class DBNET():
- def __init__(self, model_path):
- sess_options = rt.SessionOptions()
- sess_options.log_severity_level = 3
- self.sess = rt.InferenceSession(model_path, sess_options)
- self.decode_handel = SegDetectorRepresenter()
-
- def process(self, img, short_size):
-
- img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
- h, w = img.shape[:2]
- if h < w:
- scale_h = short_size / h
- tar_w = w * scale_h
- tar_w = tar_w - tar_w % 32
- tar_w = max(32, tar_w)
- scale_w = tar_w / w
- else:
- scale_w = short_size / w
- tar_h = h * scale_w
- tar_h = tar_h - tar_h % 32
- tar_h = max(32, tar_h)
- scale_h = tar_h / h
-
- img = cv2.resize(img, None, fx=scale_w, fy=scale_h)
- img = img.astype(np.float32)
-
- img /= 255.0
- img -= mean
- img /= std
- img = img.transpose(2, 0, 1)
- transformed_image = np.expand_dims(img, axis=0)
- out = self.sess.run(
- ['out1'], {'input0': transformed_image.astype(np.float32)})
- box_list, score_list = self.decode_handel(out[0][0], h, w)
- if len(box_list) > 0:
- idx = box_list.reshape(
- box_list.shape[0], -1).sum(axis=1) > 0
- box_list, score_list = box_list[idx], score_list[idx]
- else:
- box_list, score_list = [], []
- return box_list, score_list
diff --git a/arknights_mower/ocr/decode.py b/arknights_mower/ocr/decode.py
deleted file mode 100644
index 1352ad753..000000000
--- a/arknights_mower/ocr/decode.py
+++ /dev/null
@@ -1,123 +0,0 @@
-import cv2
-import numpy as np
-import pyclipper
-from shapely.geometry import Polygon
-
-
-class SegDetectorRepresenter:
- def __init__(self, thresh=0.3, box_thresh=0.5, max_candidates=1000, unclip_ratio=2.0):
- self.min_size = 3
- self.thresh = thresh
- self.box_thresh = box_thresh
- self.max_candidates = max_candidates
- self.unclip_ratio = unclip_ratio
-
- def __call__(self, pred, height, width):
- """
- batch: (image, polygons, ignore_tags
- batch: a dict produced by dataloaders.
- image: tensor of shape (N, C, H, W).
- polygons: tensor of shape (N, K, 4, 2), the polygons of objective regions.
- ignore_tags: tensor of shape (N, K), indicates whether a region is ignorable or not.
- shape: the original shape of images.
- filename: the original filenames of images.
- pred:
- binary: text region segmentation map, with shape (N, H, W)
- thresh: [if exists] thresh hold prediction with shape (N, H, W)
- thresh_binary: [if exists] binarized with threshhold, (N, H, W)
- """
-
- pred = pred[0, :, :]
- segmentation = self.binarize(pred)
-
- boxes, scores = self.boxes_from_bitmap(
- pred, segmentation, width, height)
-
- return boxes, scores
-
- def binarize(self, pred):
- return pred > self.thresh
-
- def boxes_from_bitmap(self, pred, bitmap, dest_width, dest_height):
- """
- bitmap: single map with shape (H, W), whose values are binarized as {0, 1}
- """
-
- assert len(bitmap.shape) == 2
- height, width = bitmap.shape
- contours, _ = cv2.findContours(
- (bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
- num_contours = min(len(contours), self.max_candidates)
- boxes = np.zeros((num_contours, 4, 2), dtype=np.int16)
- scores = np.zeros((num_contours,), dtype=np.float32)
- for index in range(num_contours):
- contour = contours[index].squeeze(1)
- points, sside = self.get_mini_boxes(contour)
- if sside < self.min_size:
- continue
- points = np.array(points)
- score = self.box_score_fast(pred, contour)
- if self.box_thresh > score:
- continue
- box = self.unclip(
- points, unclip_ratio=self.unclip_ratio).reshape(-1, 1, 2)
- box, sside = self.get_mini_boxes(box)
- if sside < self.min_size + 2:
- continue
- box = np.array(box)
- if not isinstance(dest_width, int):
- dest_width = dest_width.item()
- dest_height = dest_height.item()
-
- box[:, 0] = np.clip(
- np.round(box[:, 0] / width * dest_width), 0, dest_width)
- box[:, 1] = np.clip(
- np.round(box[:, 1] / height * dest_height), 0, dest_height)
- boxes[index, :, :] = box.astype(np.int16)
- scores[index] = score
- return boxes, scores
-
- def unclip(self, box, unclip_ratio=1.5):
- poly = Polygon(box)
-
- distance = poly.area * unclip_ratio / (poly.length)
- offset = pyclipper.PyclipperOffset()
- offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
- expanded = np.array(offset.Execute(distance))
- return expanded
-
- def get_mini_boxes(self, contour):
- bounding_box = cv2.minAreaRect(contour)
- points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
-
- index_1, index_2, index_3, index_4 = 0, 1, 2, 3
- if points[1][1] > points[0][1]:
- index_1 = 0
- index_4 = 1
- else:
- index_1 = 1
- index_4 = 0
- if points[3][1] > points[2][1]:
- index_2 = 2
- index_3 = 3
- else:
- index_2 = 3
- index_3 = 2
-
- box = [points[index_1], points[index_2],
- points[index_3], points[index_4]]
- return box, min(bounding_box[1])
-
- def box_score_fast(self, bitmap, _box):
- h, w = bitmap.shape[:2]
- box = _box.copy()
- xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int), 0, w - 1)
- xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int), 0, w - 1)
- ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int), 0, h - 1)
- ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int), 0, h - 1)
-
- mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8)
- box[:, 0] = box[:, 0] - xmin
- box[:, 1] = box[:, 1] - ymin
- cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1)
- return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0]
diff --git a/arknights_mower/ocr/keys.py b/arknights_mower/ocr/keys.py
deleted file mode 100644
index 27f7bb76b..000000000
--- a/arknights_mower/ocr/keys.py
+++ /dev/null
@@ -1 +0,0 @@
-alphabetChinese = u'\'疗绚诚娇溜题贿者廖更纳加奉公一就汴计与路房原妇208-7其>:],,骑刈全消昏傈安久钟嗅不影处驽蜿资关椤地瘸专问忖票嫉炎韵要月田节陂鄙捌备拳伺眼网盎大傍心东愉汇蹿科每业里航晏字平录先13彤鲶产稍督腴有象岳注绍在泺文定核名水过理让偷率等这发”为含肥酉相鄱七编猥锛日镀蒂掰倒辆栾栗综涩州雌滑馀了机块司宰甙兴矽抚保用沧秩如收息滥页疑埠!!姥异橹钇向下跄的椴沫国绥獠报开民蜇何分凇长讥藏掏施羽中讲派嘟人提浼间世而古多倪唇饯控庚首赛蜓味断制觉技替艰溢潮夕钺外摘枋动双单啮户枇确锦曜杜或能效霜盒然侗电晁放步鹃新杖蜂吒濂瞬评总隍对独合也是府青天诲墙组滴级邀帘示已时骸仄泅和遨店雇疫持巍踮境只亨目鉴崤闲体泄杂作般轰化解迂诿蛭璀腾告版服省师小规程线海办引二桧牌砺洄裴修图痫胡许犊事郛基柴呼食研奶律蛋因葆察戏褒戒再李骁工貂油鹅章啄休场给睡纷豆器捎说敏学会浒设诊格廓查来霓室溆¢诡寥焕舜柒狐回戟砾厄实翩尿五入径惭喹股宇篝|;美期云九祺扮靠锝槌系企酰阊暂蚕忻豁本羹执条钦H獒限进季楦于芘玖铋茯未答粘括样精欠矢甥帷嵩扣令仔风皈行支部蓉刮站蜡救钊汗松嫌成可.鹤院从交政怕活调球局验髌第韫谗串到圆年米/*友忿检区看自敢刃个兹弄流留同没齿星聆轼湖什三建蛔儿椋汕震颧鲤跟力情璺铨陪务指族训滦鄣濮扒商箱十召慷辗所莞管护臭横硒嗓接侦六露党馋驾剖高侬妪幂猗绺骐央酐孝筝课徇缰门男西项句谙瞒秃篇教碲罚声呐景前富嘴鳌稀免朋啬睐去赈鱼住肩愕速旁波厅健茼厥鲟谅投攸炔数方击呋谈绩别愫僚躬鹧胪炳招喇膨泵蹦毛结54谱识陕粽婚拟构且搜任潘比郢妨醪陀桔碘扎选哈骷楷亿明缆脯监睫逻婵共赴淝凡惦及达揖谩澹减焰蛹番祁柏员禄怡峤龙白叽生闯起细装谕竟聚钙上导渊按艾辘挡耒盹饪臀记邮蕙受各医搂普滇朗茸带翻酚(光堤墟蔷万幻〓瑙辈昧盏亘蛀吉铰请子假闻税井诩哨嫂好面琐校馊鬣缂营访炖占农缀否经钚棵趟张亟吏茶谨捻论迸堂玉信吧瞠乡姬寺咬溏苄皿意赉宝尔钰艺特唳踉都荣倚登荐丧奇涵批炭近符傩感道着菊虹仲众懈濯颞眺南释北缝标既茗整撼迤贲挎耱拒某妍卫哇英矶藩治他元领膜遮穗蛾飞荒棺劫么市火温拈棚洼转果奕卸迪伸泳斗邡侄涨屯萋胭氡崮枞惧冒彩斜手豚随旭淑妞形菌吲沱争驯歹挟兆柱传至包内响临红功弩衡寂禁老棍耆渍织害氵渑布载靥嗬虽苹咨娄库雉榜帜嘲套瑚亲簸欧边6腿旮抛吹瞳得镓梗厨继漾愣憨士策窑抑躯襟脏参贸言干绸鳄穷藜音折详)举悍甸癌黎谴死罩迁寒驷袖媒蒋掘模纠恣观祖蛆碍位稿主澧跌筏京锏帝贴证糠才黄鲸略炯饱四出园犀牧容汉杆浈汰瑷造虫瘩怪驴济应花沣谔夙旅价矿以考su呦晒巡茅准肟瓴詹仟褂译桌混宁怦郑抿些余鄂饴攒珑群阖岔琨藓预环洮岌宀杲瀵最常囡周踊女鼓袭喉简范薯遐疏粱黜禧法箔斤遥汝奥直贞撑置绱集她馅逗钧橱魉[恙躁唤9旺膘待脾惫购吗依盲度瘿蠖俾之镗拇鲵厝簧续款展啃表剔品钻腭损清锶统涌寸滨贪链吠冈伎迥咏吁览防迅失汾阔逵绀蔑列川凭努熨揪利俱绉抢鸨我即责膦易毓鹊刹玷岿空嘞绊排术估锷违们苟铜播肘件烫审鲂广像铌惰铟巳胍鲍康憧色恢想拷尤疳知SYFDA峄裕帮握搔氐氘难墒沮雨叁缥悴藐湫娟苑稠颛簇后阕闭蕤缚怎佞码嘤蔡痊舱螯帕赫昵升烬岫、疵蜻髁蕨隶烛械丑盂梁强鲛由拘揉劭龟撤钩呕孛费妻漂求阑崖秤甘通深补赃坎床啪承吼量暇钼烨阂擎脱逮称P神属矗华届狍葑汹育患窒蛰佼静槎运鳗庆逝曼疱克代官此麸耧蚌晟例础榛副测唰缢迹灬霁身岁赭扛又菡乜雾板读陷徉贯郁虑变钓菜圾现琢式乐维渔浜左吾脑钡警T啵拴偌漱湿硕止骼魄积燥联踢玛|则窿见振畿送班钽您赵刨印讨踝籍谡舌崧汽蔽沪酥绒怖财帖肱私莎勋羔霸励哼帐将帅渠纪婴娩岭厘滕吻伤坝冠戊隆瘁介涧物黍并姗奢蹑掣垸锴命箍捉病辖琰眭迩艘绌繁寅若毋思诉类诈燮轲酮狂重反职筱县委磕绣奖晋濉志徽肠呈獐坻口片碰几村柿劳料获亩惕晕厌号罢池正鏖煨家棕复尝懋蜥锅岛扰队坠瘾钬@卧疣镇譬冰彷频黯据垄采八缪瘫型熹砰楠襁箐但嘶绳啤拍盥穆傲洗盯塘怔筛丿台恒喂葛永¥烟酒桦书砂蚝缉态瀚袄圳轻蛛超榧遛姒奘铮右荽望偻卡丶氰附做革索戚坨桷唁垅榻岐偎坛莨山殊微骇陈爨推嗝驹澡藁呤卤嘻糅逛侵郓酌德摇※鬃被慨殡羸昌泡戛鞋河宪沿玲鲨翅哽源铅语照邯址荃佬顺鸳町霭睾瓢夸椁晓酿痈咔侏券噎湍签嚷离午尚社锤背孟使浪缦潍鞅军姹驶笑鳟鲁》孽钜绿洱礴焯椰颖囔乌孔巴互性椽哞聘昨早暮胶炀隧低彗昝铁呓氽藉喔癖瑗姨权胱韦堑蜜酋楝砝毁靓歙锲究屋喳骨辨碑武鸠宫辜烊适坡殃培佩供走蜈迟翼况姣凛浔吃飘债犟金促苛崇坂莳畔绂兵蠕斋根砍亢欢恬崔剁餐榫快扶‖濒缠鳜当彭驭浦篮昀锆秸钳弋娣瞑夷龛苫拱致%嵊障隐弑初娓抉汩累蓖"唬助苓昙押毙破城郧逢嚏獭瞻溱婿赊跨恼璧萃姻貉灵炉密氛陶砸谬衔点琛沛枳层岱诺脍榈埂征冷裁打蹴素瘘逞蛐聊激腱萘踵飒蓟吆取咙簋涓矩曝挺揣座你史舵焱尘苏笈脚溉榨诵樊邓焊义庶儋蟋蒲赦呷杞诠豪还试颓茉太除紫逃痴草充鳕珉祗墨渭烩蘸慕璇镶穴嵘恶骂险绋幕碉肺戳刘潞秣纾潜銮洛须罘销瘪汞兮屉r林厕质探划狸殚善煊烹〒锈逯宸辍泱柚袍远蹋嶙绝峥娥缍雀徵认镱谷=贩勉撩鄯斐洋非祚泾诒饿撬威晷搭芍锥笺蓦候琊档礁沼卵荠忑朝凹瑞头仪弧孵畏铆突衲车浩气茂悖厢枕酝戴湾邹飚攘锂写宵翁岷无喜丈挑嗟绛殉议槽具醇淞笃郴阅饼底壕砚弈询缕庹翟零筷暨舟闺甯撞麂茌蔼很珲捕棠角阉媛娲诽剿尉爵睬韩诰匣危糍镯立浏阳少盆舔擘匪申尬铣旯抖赘瓯居ˇ哮游锭茏歌坏甚秒舞沙仗劲潺阿燧郭嗖霏忠材奂耐跺砀输岖媳氟极摆灿今扔腻枝奎药熄吨话q额慑嘌协喀壳埭视著於愧陲翌峁颅佛腹聋侯咎叟秀颇存较罪哄岗扫栏钾羌己璨枭霉煌涸衿键镝益岢奏连夯睿冥均糖狞蹊稻爸刿胥煜丽肿璃掸跚灾垂樾濑乎莲窄犹撮战馄软络显鸢胸宾妲恕埔蝌份遇巧瞟粒恰剥桡博讯凯堇阶滤卖斌骚彬兑磺樱舷两娱福仃差找桁÷净把阴污戬雷碓蕲楚罡焖抽妫咒仑闱尽邑菁爱贷沥鞑牡嗉崴骤塌嗦订拮滓捡锻次坪杩臃箬融珂鹗宗枚降鸬妯阄堰盐毅必杨崃俺甬状莘货耸菱腼铸唏痤孚澳懒溅翘疙杷淼缙骰喊悉砻坷艇赁界谤纣宴晃茹归饭梢铡街抄肼鬟苯颂撷戈炒咆茭瘙负仰客琉铢封卑珥椿镧窨鬲寿御袤铃萎砖餮脒裳肪孕嫣馗嵇恳氯江石褶冢祸阻狈羞银靳透咳叼敷芷啥它瓤兰痘懊逑肌往捺坊甩呻〃沦忘膻祟菅剧崆智坯臧霍墅攻眯倘拢骠铐庭岙瓠′缺泥迢捶??郏喙掷沌纯秘种听绘固螨团香盗妒埚蓝拖旱荞铀血遏汲辰叩拽幅硬惶桀漠措泼唑齐肾念酱虚屁耶旗砦闵婉馆拭绅韧忏窝醋葺顾辞倜堆辋逆玟贱疾董惘倌锕淘嘀莽俭笏绑鲷杈择蟀粥嗯驰逾案谪褓胫哩昕颚鲢绠躺鹄崂儒俨丝尕泌啊萸彰幺吟骄苣弦脊瑰〈诛镁析闪剪侧哟框螃守嬗燕狭铈缮概迳痧鲲俯售笼痣扉挖满咋援邱扇歪便玑绦峡蛇叨〖泽胃斓喋怂坟猪该蚬炕弥赞棣晔娠挲狡创疖铕镭稷挫弭啾翔粉履苘哦楼秕铂土锣瘟挣栉习享桢袅磨桂谦延坚蔚噗署谟猬钎恐嬉雒倦衅亏璩睹刻殿王算雕麻丘柯骆丸塍谚添鲈垓桎蚯芥予飕镦谌窗醚菀亮搪莺蒿羁足J真轶悬衷靛翊掩哒炅掐冼妮l谐稚荆擒犯陵虏浓崽刍陌傻孜千靖演矜钕煽杰酗渗伞栋俗泫戍罕沾疽灏煦芬磴叱阱榉湃蜀叉醒彪租郡篷屎良垢隗弱陨峪砷掴颁胎雯绵贬沐撵隘篙暖曹陡栓填臼彦瓶琪潼哪鸡摩啦俟锋域耻蔫疯纹撇毒绶痛酯忍爪赳歆嘹辕烈册朴钱吮毯癜娃谀邵厮炽璞邃丐追词瓒忆轧芫谯喷弟半冕裙掖墉绮寝苔势顷褥切衮君佳嫒蚩霞佚洙逊镖暹唛&殒顶碗獗轭铺蛊废恹汨崩珍那杵曲纺夏薰傀闳淬姘舀拧卷楂恍讪厩寮篪赓乘灭盅鞣沟慎挂饺鼾杳树缨丛絮娌臻嗳篡侩述衰矛圈蚜匕筹匿濞晨叶骋郝挚蚴滞增侍描瓣吖嫦蟒匾圣赌毡癞恺百曳需篓肮庖帏卿驿遗蹬鬓骡歉芎胳屐禽烦晌寄媾狄翡苒船廉终痞殇々畦饶改拆悻萄£瓿乃訾桅匮溧拥纱铍骗蕃龋缬父佐疚栎醍掳蓄x惆颜鲆榆〔猎敌暴谥鲫贾罗玻缄扦芪癣落徒臾恿猩托邴肄牵春陛耀刊拓蓓邳堕寇枉淌啡湄兽酷萼碚濠萤夹旬戮梭琥椭昔勺蜊绐晚孺僵宣摄冽旨萌忙蚤眉噼蟑付契瓜悼颡壁曾窕颢澎仿俑浑嵌浣乍碌褪乱蔟隙玩剐葫箫纲围伐决伙漩瑟刑肓镳缓蹭氨皓典畲坍铑檐塑洞倬储胴淳戾吐灼惺妙毕珐缈虱盖羰鸿磅谓髅娴苴唷蚣霹抨贤唠犬誓逍庠逼麓籼釉呜碧秧氩摔霄穸纨辟妈映完牛缴嗷炊恩荔茆掉紊慌莓羟阙萁磐另蕹辱鳐湮吡吩唐睦垠舒圜冗瞿溺芾囱匠僳汐菩饬漓黑霰浸濡窥毂蒡兢驻鹉芮诙迫雳厂忐臆猴鸣蚪栈箕羡渐莆捍眈哓趴蹼埕嚣骛宏淄斑噜严瑛垃椎诱压庾绞焘廿抡迄棘夫纬锹眨瞌侠脐竞瀑孳骧遁姜颦荪滚萦伪逸粳爬锁矣役趣洒颔诏逐奸甭惠攀蹄泛尼拼阮鹰亚颈惑勒〉际肛爷刚钨丰养冶鲽辉蔻画覆皴妊麦返醉皂擀〗酶凑粹悟诀硖港卜z杀涕±舍铠抵弛段敝镐奠拂轴跛袱et沉菇俎薪峦秭蟹历盟菠寡液肢喻染裱悱抱氙赤捅猛跑氮谣仁尺辊窍烙衍架擦倏璐瑁币楞胖夔趸邛惴饕虔蝎§哉贝宽辫炮扩饲籽魏菟锰伍猝末琳哚蛎邂呀姿鄞却歧仙恸椐森牒寤袒婆虢雅钉朵贼欲苞寰故龚坭嘘咫礼硷兀睢汶’铲烧绕诃浃钿哺柜讼颊璁腔洽咐脲簌筠镣玮鞠谁兼姆挥梯蝴谘漕刷躏宦弼b垌劈麟莉揭笙渎仕嗤仓配怏抬错泯镊孰猿邪仍秋鼬壹歇吵炼<尧射柬廷胧霾凳隋肚浮梦祥株堵退L鹫跎凶毽荟炫栩玳甜沂鹿顽伯爹赔蛴徐匡欣狰缸雹蟆疤默沤啜痂衣禅wih辽葳黝钗停沽棒馨颌肉吴硫悯劾娈马啧吊悌镑峭帆瀣涉咸疸滋泣翦拙癸钥蜒+尾庄凝泉婢渴谊乞陆锉糊鸦淮IBN晦弗乔庥葡尻席橡傣渣拿惩麋斛缃矮蛏岘鸽姐膏催奔镒喱蠡摧钯胤柠拐璋鸥卢荡倾^_珀逄萧塾掇贮笆聂圃冲嵬M滔笕值炙偶蜱搐梆汪蔬腑鸯蹇敞绯仨祯谆梧糗鑫啸豺囹猾巢柄瀛筑踌沭暗苁鱿蹉脂蘖牢热木吸溃宠序泞偿拜檩厚朐毗螳吞媚朽担蝗橘畴祈糟盱隼郜惜珠裨铵焙琚唯咚噪骊丫滢勤棉呸咣淀隔蕾窈饨挨煅短匙粕镜赣撕墩酬馁豌颐抗酣氓佑搁哭递耷涡桃贻碣截瘦昭镌蔓氚甲猕蕴蓬散拾纛狼猷铎埋旖矾讳囊糜迈粟蚂紧鲳瘢栽稼羊锄斟睁桥瓮蹙祉醺鼻昱剃跳篱跷蒜翎宅晖嗑壑峻癫屏狠陋袜途憎祀莹滟佶溥臣约盛峰磁慵婪拦莅朕鹦粲裤哎疡嫖琵窟堪谛嘉儡鳝斩郾驸酊妄胜贺徙傅噌钢栅庇恋匝巯邈尸锚粗佟蛟薹纵蚊郅绢锐苗俞篆淆膀鲜煎诶秽寻涮刺怀噶巨褰魅灶灌桉藕谜舸薄搀恽借牯痉渥愿亓耘杠柩锔蚶钣珈喘蹒幽赐稗晤莱泔扯肯菪裆腩豉疆骜腐倭珏唔粮亡润慰伽橄玄誉醐胆龊粼塬陇彼削嗣绾芽妗垭瘴爽薏寨龈泠弹赢漪猫嘧涂恤圭茧烽屑痕巾赖荸凰腮畈亵蹲偃苇澜艮换骺烘苕梓颉肇哗悄氤涠葬屠鹭植竺佯诣鲇瘀鲅邦移滁冯耕癔戌茬沁巩悠湘洪痹锟循谋腕鳃钠捞焉迎碱伫急榷奈邝卯辄皲卟醛畹忧稳雄昼缩阈睑扌耗曦涅捏瞧邕淖漉铝耦禹湛喽莼琅诸苎纂硅始嗨傥燃臂赅嘈呆贵屹壮肋亍蚀卅豹腆邬迭浊}童螂捐圩勐触寞汊壤荫膺渌芳懿遴螈泰蓼蛤茜舅枫朔膝眙避梅判鹜璜牍缅垫藻黔侥惚懂踩腰腈札丞唾慈顿摹荻琬~斧沈滂胁胀幄莜Z匀鄄掌绰茎焚赋萱谑汁铒瞎夺蜗野娆冀弯篁懵灞隽芡脘俐辩芯掺喏膈蝈觐悚踹蔗熠鼠呵抓橼峨畜缔禾崭弃熊摒凸拗穹蒙抒祛劝闫扳阵醌踪喵侣搬仅荧赎蝾琦买婧瞄寓皎冻赝箩莫瞰郊笫姝筒枪遣煸袋舆痱涛母〇启践耙绲盘遂昊搞槿诬纰泓惨檬亻越Co憩熵祷钒暧塔阗胰咄娶魔琶钞邻扬杉殴咽弓〆髻】吭揽霆拄殖脆彻岩芝勃辣剌钝嘎甄佘皖伦授徕憔挪皇庞稔芜踏溴兖卒擢饥鳞煲‰账颗叻斯捧鳍琮讹蛙纽谭酸兔莒睇伟觑羲嗜宜褐旎辛卦诘筋鎏溪挛熔阜晰鳅丢奚灸呱献陉黛鸪甾萨疮拯洲疹辑叙恻谒允柔烂氏逅漆拎惋扈湟纭啕掬擞哥忽涤鸵靡郗瓷扁廊怨雏钮敦E懦憋汀拚啉腌岸f痼瞅尊咀眩飙忌仝迦熬毫胯篑茄腺凄舛碴锵诧羯後漏汤宓仞蚁壶谰皑铄棰罔辅晶苦牟闽\烃饮聿丙蛳朱煤涔鳖犁罐荼砒淦妤黏戎孑婕瑾戢钵枣捋砥衩狙桠稣阎肃梏诫孪昶婊衫嗔侃塞蜃樵峒貌屿欺缫阐栖诟珞荭吝萍嗽恂啻蜴磬峋俸豫谎徊镍韬魇晴U囟猜蛮坐囿伴亭肝佗蝠妃胞滩榴氖垩苋砣扪馏姓轩厉夥侈禀垒岑赏钛辐痔披纸碳“坞蠓挤荥沅悔铧帼蒌蝇apyng哀浆瑶凿桶馈皮奴苜佤伶晗铱炬优弊氢恃甫攥端锌灰稹炝曙邋亥眶碾拉萝绔捷浍腋姑菖凌涞麽锢桨潢绎镰殆锑渝铬困绽觎匈糙暑裹鸟盔肽迷綦『亳佝俘钴觇骥仆疝跪婶郯瀹唉脖踞针晾忒扼瞩叛椒疟嗡邗肆跆玫忡捣咧唆艄蘑潦笛阚沸泻掊菽贫斥髂孢镂赂麝鸾屡衬苷恪叠希粤爻喝茫惬郸绻庸撅碟宄妹膛叮饵崛嗲椅冤搅咕敛尹垦闷蝉霎勰败蓑泸肤鹌幌焦浠鞍刁舰乙竿裔。茵函伊兄丨娜匍謇莪宥似蝽翳酪翠粑薇祢骏赠叫Q噤噻竖芗莠潭俊羿耜O郫趁嗪囚蹶芒洁笋鹑敲硝啶堡渲揩』携宿遒颍扭棱割萜蔸葵琴捂饰衙耿掠募岂窖涟蔺瘤柞瞪怜匹距楔炜哆秦缎幼茁绪痨恨楸娅瓦桩雪嬴伏榔妥铿拌眠雍缇‘卓搓哌觞噩屈哧髓咦巅娑侑淫膳祝勾姊莴胄疃薛蜷胛巷芙芋熙闰勿窃狱剩钏幢陟铛慧靴耍k浙浇飨惟绗祜澈啼咪磷摞诅郦抹跃壬吕肖琏颤尴剡抠凋赚泊津宕殷倔氲漫邺涎怠$垮荬遵俏叹噢饽蜘孙筵疼鞭羧牦箭潴c眸祭髯啖坳愁芩驮倡巽穰沃胚怒凤槛剂趵嫁v邢灯鄢桐睽檗锯槟婷嵋圻诗蕈颠遭痢芸怯馥竭锗徜恭遍籁剑嘱苡龄僧桑潸弘澶楹悲讫愤腥悸谍椹呢桓葭攫阀翰躲敖柑郎笨橇呃魁燎脓葩磋垛玺狮沓砜蕊锺罹蕉翱虐闾巫旦茱嬷枯鹏贡芹汛矫绁拣禺佃讣舫惯乳趋疲挽岚虾衾蠹蹂飓氦铖孩稞瑜壅掀勘妓畅髋W庐牲蓿榕练垣唱邸菲昆婺穿绡麒蚱掂愚泷涪漳妩娉榄讷觅旧藤煮呛柳腓叭庵烷阡罂蜕擂猖咿媲脉【沏貅黠熏哲烁坦酵兜×潇撒剽珩圹乾摸樟帽嗒襄魂轿憬锡〕喃皆咖隅脸残泮袂鹂珊囤捆咤误徨闹淙芊淋怆囗拨梳渤RG绨蚓婀幡狩麾谢唢裸旌伉纶裂驳砼咛澄樨蹈宙澍倍貔操勇蟠摈砧虬够缁悦藿撸艹摁淹豇虎榭ˉ吱d°喧荀踱侮奋偕饷犍惮坑璎徘宛妆袈倩窦昂荏乖K怅撰鳙牙袁酞X痿琼闸雁趾荚虻涝《杏韭偈烤绫鞘卉症遢蓥诋杭荨匆竣簪辙敕虞丹缭咩黟m淤瑕咂铉硼茨嶂痒畸敬涿粪窘熟叔嫔盾忱裘憾梵赡珙咯娘庙溯胺葱痪摊荷卞乒髦寐铭坩胗枷爆溟嚼羚砬轨惊挠罄竽菏氧浅楣盼枢炸阆杯谏噬淇渺俪秆墓泪跻砌痰垡渡耽釜讶鳎煞呗韶舶绷鹳缜旷铊皱龌檀霖奄槐艳蝶旋哝赶骞蚧腊盈丁`蜚矸蝙睨嚓僻鬼醴夜彝磊笔拔栀糕厦邰纫逭纤眦膊馍躇烯蘼冬诤暄骶哑瘠」臊丕愈咱螺擅跋搏硪谄笠淡嘿骅谧鼎皋姚歼蠢驼耳胬挝涯狗蒽孓犷凉芦箴铤孤嘛坤V茴朦挞尖橙诞搴碇洵浚帚蜍漯柘嚎讽芭荤咻祠秉跖埃吓糯眷馒惹娼鲑嫩讴轮瞥靶褚乏缤宋帧删驱碎扑俩俄偏涣竹噱皙佰渚唧斡#镉刀崎筐佣夭贰肴峙哔艿匐牺镛缘仡嫡劣枸堀梨簿鸭蒸亦稽浴{衢束槲j阁揍疥棋潋聪窜乓睛插冉阪苍搽「蟾螟幸仇樽撂慢跤幔俚淅覃觊溶妖帛侨曰妾泗 ·'
diff --git a/arknights_mower/ocr/model.py b/arknights_mower/ocr/model.py
deleted file mode 100644
index 3c8fdbb8b..000000000
--- a/arknights_mower/ocr/model.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import copy
-import traceback
-
-import cv2
-import numpy as np
-from PIL import Image
-
-from ..utils.log import logger
-from .config import crnn_model_path, dbnet_model_path
-from .crnn import CRNNHandle
-from .dbnet import DBNET
-from .utils import fix
-
-
-def sorted_boxes(dt_boxes):
- """
- Sort text boxes in order from top to bottom, left to right
- args:
- dt_boxes(array):detected text boxes with shape [4, 2]
- return:
- sorted boxes(array) with shape [4, 2]
- """
- num_boxes = dt_boxes.shape[0]
- sorted_boxes = sorted(dt_boxes, key=lambda x: (x[0][1], x[0][0]))
- _boxes = list(sorted_boxes)
-
- for i in range(num_boxes - 1):
- if abs(_boxes[i+1][0][1] - _boxes[i][0][1]) < 10 and \
- (_boxes[i + 1][0][0] < _boxes[i][0][0]):
- tmp = _boxes[i]
- _boxes[i] = _boxes[i + 1]
- _boxes[i + 1] = tmp
- return _boxes
-
-
-def get_rotate_crop_image(img, points):
- img_height, img_width = img.shape[0:2]
- left = int(np.min(points[:, 0]))
- right = int(np.max(points[:, 0]))
- top = int(np.min(points[:, 1]))
- bottom = int(np.max(points[:, 1]))
- img_crop = img[top:bottom, left:right, :].copy()
- points[:, 0] = points[:, 0] - left
- points[:, 1] = points[:, 1] - top
- img_crop_width = int(np.linalg.norm(points[0] - points[1]))
- img_crop_height = int(np.linalg.norm(points[0] - points[3]))
- pts_std = np.float32([[0, 0], [img_crop_width, 0],
- [img_crop_width, img_crop_height], [0, img_crop_height]])
-
- M = cv2.getPerspectiveTransform(points, pts_std)
- dst_img = cv2.warpPerspective(
- img_crop,
- M, (img_crop_width, img_crop_height),
- borderMode=cv2.BORDER_REPLICATE)
- dst_img_height, dst_img_width = dst_img.shape[0:2]
- if dst_img_height * 1.0 / dst_img_width >= 1.5:
- dst_img = np.rot90(dst_img)
- return dst_img
-
-
-class OcrHandle(object):
- def __init__(self):
- self.text_handle = DBNET(dbnet_model_path)
- self.crnn_handle = CRNNHandle(crnn_model_path)
-
- def crnnRecWithBox(self, im, boxes_list, score_list, is_rgb=False):
- results = []
- boxes_list = sorted_boxes(np.array(boxes_list))
-
- count = 1
- for (box, score) in zip(boxes_list, score_list):
-
- tmp_box = copy.deepcopy(box)
- partImg_array = get_rotate_crop_image(
- im, tmp_box.astype(np.float32))
-
- try:
- if is_rgb:
- partImg = Image.fromarray(partImg_array).convert('RGB')
- simPred = self.crnn_handle.predict_rbg(partImg)
- else:
- partImg = Image.fromarray(partImg_array).convert('L')
- simPred = self.crnn_handle.predict(partImg)
- except Exception as e:
- logger.debug(traceback.format_exc())
- continue
-
- if simPred.strip() != '':
- results.append([count, simPred, tmp_box.tolist(), score])
- count += 1
-
- return results
-
- def predict(self, img, is_rgb=False):
- short_size = min(img.shape[:-1])
- short_size = short_size // 32 * 32
- boxes_list, score_list = self.text_handle.process(img, short_size)
- result = self.crnnRecWithBox(img, boxes_list, score_list, is_rgb)
- for i in range(len(result)):
- result[i][1] = fix(result[i][1])
- logger.debug(result)
- return result
diff --git a/arknights_mower/ocr/ocrspace.py b/arknights_mower/ocr/ocrspace.py
deleted file mode 100644
index 57cda0170..000000000
--- a/arknights_mower/ocr/ocrspace.py
+++ /dev/null
@@ -1,164 +0,0 @@
-import base64
-import traceback
-
-import cv2
-import numpy
-import requests
-
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError
-from .utils import fix
-
-
-class Language:
- Arabic = 'ara'
- Bulgarian = 'bul'
- Chinese_Simplified = 'chs'
- Chinese_Traditional = 'cht'
- Croatian = 'hrv'
- Danish = 'dan'
- Dutch = 'dut'
- English = 'eng'
- Finnish = 'fin'
- French = 'fre'
- German = 'ger'
- Greek = 'gre'
- Hungarian = 'hun'
- Korean = 'kor'
- Italian = 'ita'
- Japanese = 'jpn'
- Norwegian = 'nor'
- Polish = 'pol'
- Portuguese = 'por'
- Russian = 'rus'
- Slovenian = 'slv'
- Spanish = 'spa'
- Swedish = 'swe'
- Turkish = 'tur'
-
-
-class API:
- def __init__(
- self,
- endpoint='https://api.ocr.space/parse/image',
- api_key='helloworld',
- language=Language.Chinese_Simplified,
- **kwargs,
- ):
- """
- :param endpoint: API endpoint to contact
- :param api_key: API key string
- :param language: document language
- :param **kwargs: other settings to API
- """
- self.timeout = (5, 10)
- self.endpoint = endpoint
- self.payload = {
- 'isOverlayRequired': True,
- 'apikey': api_key,
- 'language': language,
- **kwargs
- }
-
- def _parse(self, raw):
- logger.debug(raw)
- if type(raw) == str:
- raise RecognizeError(raw)
- if raw['IsErroredOnProcessing']:
- raise RecognizeError(raw['ErrorMessage'][0])
- if raw['ParsedResults'][0].get('TextOverlay') is None:
- raise RecognizeError('No Result')
- # ret = []
- # for x in raw['ParsedResults'][0]['TextOverlay']['Lines']:
- # left, right, up, down = 1e30, 0, 1e30, 0
- # for w in x['Words']:
- # left = min(left, w['Left'])
- # right = max(right, w['Left'] + w['Width'])
- # up = min(up, w['Top'])
- # down = max(down, w['Top'] + w['Height'])
- # ret.append([x['LineText'], [(left + right) / 2, (up + down) / 2]])
- # return ret
- ret = [x['LineText']
- for x in raw['ParsedResults'][0]['TextOverlay']['Lines']]
- return ret
-
- def ocr_file(self, fp):
- """
- Process image from a local path.
- :param fp: A path or pointer to your file
- :return: Result in JSON format
- """
- with (open(fp, 'rb') if type(fp) == str else fp) as f:
- r = requests.post(
- self.endpoint,
- files={'filename': f},
- data=self.payload,
- timeout=self.timeout,
- )
- return self._parse(r.json())
-
- def ocr_url(self, url):
- """
- Process an image at a given URL.
- :param url: Image url
- :return: Result in JSON format.
- """
- data = self.payload
- data['url'] = url
- r = requests.post(
- self.endpoint,
- data=data,
- timeout=self.timeout,
- )
- return self._parse(r.json())
-
- def ocr_base64(self, base64image):
- """
- Process an image given as base64.
- :param base64image: Image represented as Base64
- :return: Result in JSON format.
- """
- data = self.payload
- data['base64Image'] = base64image
- r = requests.post(
- self.endpoint,
- data=data,
- timeout=self.timeout,
- )
- return self._parse(r.json())
-
- def ocr_image(self, image: numpy.ndarray):
- data = self.payload
- data['base64Image'] = 'data:image/jpg;base64,' + \
- base64.b64encode(cv2.imencode('.jpg', image)[1].tobytes()).decode()
-
- retry_times = 1
- while True:
- try:
- r = requests.post(
- self.endpoint,
- data=data,
- timeout=self.timeout,
- )
- break
- except Exception as e:
- logger.warning(e)
- logger.debug(traceback.format_exc())
- retry_times -= 1
- if retry_times > 0:
- logger.warning('重试中……')
- else:
- logger.warning('无网络或网络故障,无法连接到 OCR Space')
- return []
- try:
- return self._parse(r.json())
- except Exception as e:
- logger.debug(e)
- return []
-
- def predict(self, image, scope):
- ret = self.ocr_image(
- image[scope[0][1]:scope[2][1], scope[0][0]:scope[2][0]])
- if len(ret) == 0:
- return None
- return fix(ret[0])
diff --git a/arknights_mower/ocr/rectify.py b/arknights_mower/ocr/rectify.py
deleted file mode 100644
index 1aa5df4bd..000000000
--- a/arknights_mower/ocr/rectify.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from ..data import ocr_error
-from ..utils import config
-from ..utils.log import logger
-from .ocrspace import API, Language
-
-
-def ocr_rectify(img, pre, valid, text=''):
- """
- 调用在线 OCR 校正本地 OCR 得到的错误结果,并返回校正后的识别结果
- 若在线 OCR 依旧无法正确识别则返回 None
-
- :param img: numpy.array, 图像
- :param pre: (str, tuple), 本地 OCR 得到的错误结果,包括字符串和范围
- :param valid: list[str], 期望得到的识别结果
- :param text: str, 指定 log 信息前缀
-
- :return res: str | None, 识别的结果
- """
- logger.warning(f'{text}识别异常:正在调用在线 OCR 处理异常结果……')
-
- global ocronline
- print(config)
- ocronline = API(api_key=config.OCR_APIKEY,
- language=Language.Chinese_Simplified)
- pre_res = pre[1]
- res = ocronline.predict(img, pre[2])
- if res is None or res == pre_res:
- logger.warning(
- f'{text}识别异常:{pre_res} 为不存在的数据')
- elif res not in valid:
- logger.warning(
- f'{text}识别异常:{pre_res} 和 {res} 均为不存在的数据')
- else:
- logger.warning(
- f'{text}识别异常:{pre_res} 应为 {res}')
- ocr_error[pre_res] = res
- pre_res = res
- return pre_res
diff --git a/arknights_mower/ocr/utils.py b/arknights_mower/ocr/utils.py
deleted file mode 100644
index 37eecad77..000000000
--- a/arknights_mower/ocr/utils.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import re
-
-import numpy as np
-from PIL import Image
-
-from ..data import ocr_error
-from ..utils.log import logger
-
-
-class resizeNormalize(object):
-
- def __init__(self, size, interpolation=Image.BILINEAR):
- self.size = size
- self.interpolation = interpolation
-
- def __call__(self, img):
- size = self.size
- imgW, imgH = size
- scale = img.size[1] * 1.0 / imgH
- w = img.size[0] / scale
- w = int(w)
- img = img.resize((w, imgH), self.interpolation)
- w, h = img.size
- if w <= imgW:
- newImage = np.zeros((imgH, imgW), dtype='uint8')
- newImage[:] = 255
- newImage[:, :w] = np.array(img)
- img = Image.fromarray(newImage)
- else:
- img = img.resize((imgW, imgH), self.interpolation)
- img = np.array(img, dtype=np.float32)
- img -= 127.5
- img /= 127.5
- img = img.reshape([*img.shape, 1])
- return img
-
-
-class strLabelConverter(object):
-
- def __init__(self, alphabet):
- self.alphabet = alphabet + 'ç' # for `-1` index
- self.dict = {}
- for i, char in enumerate(alphabet):
- # NOTE: 0 is reserved for 'blank' required by wrap_ctc
- self.dict[char] = i + 1
-
- def decode(self, t, length, raw=False):
- t = t[:length]
- if raw:
- return ''.join([self.alphabet[i - 1] for i in t])
- else:
- char_list = []
- for i in range(length):
-
- if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])):
- char_list.append(self.alphabet[t[i] - 1])
- return ''.join(char_list)
-
-
-def fix(s):
- """
- 对识别结果进行简单处理,并查询是否在 ocr_error 中有过记录
- """
- s = re.sub(r'[。?!,、;:“”‘’()《》〈〉【】『』「」﹃﹄〔〕…~﹏¥-_]', '', s)
- s = re.sub(r'[\'\"\,\.\(\)]', '', s)
- if s in ocr_error.keys():
- logger.debug(f'fix with ocr_error: {s} -> {ocr_error[s]}')
- s = ocr_error[s]
- return s
diff --git a/arknights_mower/resources/12cadpa.png b/arknights_mower/resources/12cadpa.png
index 7f93c854d..602756ea6 100644
Binary files a/arknights_mower/resources/12cadpa.png and b/arknights_mower/resources/12cadpa.png differ
diff --git a/arknights_mower/resources/1800.png b/arknights_mower/resources/1800.png
new file mode 100644
index 000000000..6339a5ead
Binary files /dev/null and b/arknights_mower/resources/1800.png differ
diff --git a/arknights_mower/resources/agent_in_dormitory.png b/arknights_mower/resources/agent_in_dormitory.png
deleted file mode 100644
index 1ef999dad..000000000
Binary files a/arknights_mower/resources/agent_in_dormitory.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_name/char_009_12fce.png b/arknights_mower/resources/agent_name/char_009_12fce.png
new file mode 100644
index 000000000..5c8530f35
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_009_12fce.png differ
diff --git a/arknights_mower/resources/agent_name/char_010_chen.png b/arknights_mower/resources/agent_name/char_010_chen.png
new file mode 100644
index 000000000..ce5db409e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_010_chen.png differ
diff --git a/arknights_mower/resources/agent_name/char_017_huang.png b/arknights_mower/resources/agent_name/char_017_huang.png
new file mode 100644
index 000000000..7c4996722
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_017_huang.png differ
diff --git a/arknights_mower/resources/agent_name/char_102_texas.png b/arknights_mower/resources/agent_name/char_102_texas.png
new file mode 100644
index 000000000..b0c338fa4
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_102_texas.png differ
diff --git a/arknights_mower/resources/agent_name/char_103_angel.png b/arknights_mower/resources/agent_name/char_103_angel.png
new file mode 100644
index 000000000..1024776e3
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_103_angel.png differ
diff --git a/arknights_mower/resources/agent_name/char_107_liskam.png b/arknights_mower/resources/agent_name/char_107_liskam.png
new file mode 100644
index 000000000..0cc62b0cd
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_107_liskam.png differ
diff --git a/arknights_mower/resources/agent_name/char_108_silent.png b/arknights_mower/resources/agent_name/char_108_silent.png
new file mode 100644
index 000000000..2cee1eb6d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_108_silent.png differ
diff --git a/arknights_mower/resources/agent_name/char_109_fmout.png b/arknights_mower/resources/agent_name/char_109_fmout.png
new file mode 100644
index 000000000..ecd6745d2
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_109_fmout.png differ
diff --git a/arknights_mower/resources/agent_name/char_112_siege.png b/arknights_mower/resources/agent_name/char_112_siege.png
new file mode 100644
index 000000000..ad3a88a20
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_112_siege.png differ
diff --git a/arknights_mower/resources/agent_name/char_115_headbr.png b/arknights_mower/resources/agent_name/char_115_headbr.png
new file mode 100644
index 000000000..c03ec0b12
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_115_headbr.png differ
diff --git a/arknights_mower/resources/agent_name/char_117_myrrh.png b/arknights_mower/resources/agent_name/char_117_myrrh.png
new file mode 100644
index 000000000..9ae95e4d2
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_117_myrrh.png differ
diff --git a/arknights_mower/resources/agent_name/char_118_yuki.png b/arknights_mower/resources/agent_name/char_118_yuki.png
new file mode 100644
index 000000000..6de961ae5
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_118_yuki.png differ
diff --git a/arknights_mower/resources/agent_name/char_120_hibisc.png b/arknights_mower/resources/agent_name/char_120_hibisc.png
new file mode 100644
index 000000000..0321793cd
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_120_hibisc.png differ
diff --git a/arknights_mower/resources/agent_name/char_121_lava.png b/arknights_mower/resources/agent_name/char_121_lava.png
new file mode 100644
index 000000000..c29ea8593
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_121_lava.png differ
diff --git a/arknights_mower/resources/agent_name/char_122_beagle.png b/arknights_mower/resources/agent_name/char_122_beagle.png
new file mode 100644
index 000000000..df3d63365
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_122_beagle.png differ
diff --git a/arknights_mower/resources/agent_name/char_123_fang.png b/arknights_mower/resources/agent_name/char_123_fang.png
new file mode 100644
index 000000000..11b9e2968
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_123_fang.png differ
diff --git a/arknights_mower/resources/agent_name/char_124_kroos.png b/arknights_mower/resources/agent_name/char_124_kroos.png
new file mode 100644
index 000000000..c85c03f40
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_124_kroos.png differ
diff --git a/arknights_mower/resources/agent_name/char_126_shotst.png b/arknights_mower/resources/agent_name/char_126_shotst.png
new file mode 100644
index 000000000..3d79a7717
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_126_shotst.png differ
diff --git a/arknights_mower/resources/agent_name/char_127_estell.png b/arknights_mower/resources/agent_name/char_127_estell.png
new file mode 100644
index 000000000..3f4c9c595
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_127_estell.png differ
diff --git a/arknights_mower/resources/agent_name/char_128_plosis.png b/arknights_mower/resources/agent_name/char_128_plosis.png
new file mode 100644
index 000000000..ea6438ef2
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_128_plosis.png differ
diff --git a/arknights_mower/resources/agent_name/char_129_bluep.png b/arknights_mower/resources/agent_name/char_129_bluep.png
new file mode 100644
index 000000000..fd99bf9ee
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_129_bluep.png differ
diff --git a/arknights_mower/resources/agent_name/char_130_doberm.png b/arknights_mower/resources/agent_name/char_130_doberm.png
new file mode 100644
index 000000000..8e90f240e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_130_doberm.png differ
diff --git a/arknights_mower/resources/agent_name/char_133_mm.png b/arknights_mower/resources/agent_name/char_133_mm.png
new file mode 100644
index 000000000..30f4f555f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_133_mm.png differ
diff --git a/arknights_mower/resources/agent_name/char_134_ifrit.png b/arknights_mower/resources/agent_name/char_134_ifrit.png
new file mode 100644
index 000000000..e845dd99f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_134_ifrit.png differ
diff --git a/arknights_mower/resources/agent_name/char_136_hsguma.png b/arknights_mower/resources/agent_name/char_136_hsguma.png
new file mode 100644
index 000000000..029af65db
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_136_hsguma.png differ
diff --git a/arknights_mower/resources/agent_name/char_137_brownb.png b/arknights_mower/resources/agent_name/char_137_brownb.png
new file mode 100644
index 000000000..90071caf5
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_137_brownb.png differ
diff --git a/arknights_mower/resources/agent_name/char_141_nights.png b/arknights_mower/resources/agent_name/char_141_nights.png
new file mode 100644
index 000000000..d1bae315f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_141_nights.png differ
diff --git a/arknights_mower/resources/agent_name/char_143_ghost.png b/arknights_mower/resources/agent_name/char_143_ghost.png
new file mode 100644
index 000000000..2dd292d2f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_143_ghost.png differ
diff --git a/arknights_mower/resources/agent_name/char_144_red.png b/arknights_mower/resources/agent_name/char_144_red.png
new file mode 100644
index 000000000..e1a582b69
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_144_red.png differ
diff --git a/arknights_mower/resources/agent_name/char_145_prove.png b/arknights_mower/resources/agent_name/char_145_prove.png
new file mode 100644
index 000000000..ce7cf4635
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_145_prove.png differ
diff --git a/arknights_mower/resources/agent_name/char_147_shining.png b/arknights_mower/resources/agent_name/char_147_shining.png
new file mode 100644
index 000000000..4d18acd08
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_147_shining.png differ
diff --git a/arknights_mower/resources/agent_name/char_148_nearl.png b/arknights_mower/resources/agent_name/char_148_nearl.png
new file mode 100644
index 000000000..fcbc06da7
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_148_nearl.png differ
diff --git a/arknights_mower/resources/agent_name/char_149_scave.png b/arknights_mower/resources/agent_name/char_149_scave.png
new file mode 100644
index 000000000..1037ed607
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_149_scave.png differ
diff --git a/arknights_mower/resources/agent_name/char_150_snakek.png b/arknights_mower/resources/agent_name/char_150_snakek.png
new file mode 100644
index 000000000..1c79e3317
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_150_snakek.png differ
diff --git a/arknights_mower/resources/agent_name/char_151_myrtle.png b/arknights_mower/resources/agent_name/char_151_myrtle.png
new file mode 100644
index 000000000..a6d0d35a9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_151_myrtle.png differ
diff --git a/arknights_mower/resources/agent_name/char_155_tiger.png b/arknights_mower/resources/agent_name/char_155_tiger.png
new file mode 100644
index 000000000..c3ace51de
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_155_tiger.png differ
diff --git a/arknights_mower/resources/agent_name/char_158_milu.png b/arknights_mower/resources/agent_name/char_158_milu.png
new file mode 100644
index 000000000..a33d20e9f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_158_milu.png differ
diff --git a/arknights_mower/resources/agent_name/char_163_hpsts.png b/arknights_mower/resources/agent_name/char_163_hpsts.png
new file mode 100644
index 000000000..95fd56d23
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_163_hpsts.png differ
diff --git a/arknights_mower/resources/agent_name/char_164_nightm.png b/arknights_mower/resources/agent_name/char_164_nightm.png
new file mode 100644
index 000000000..670017e95
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_164_nightm.png differ
diff --git a/arknights_mower/resources/agent_name/char_171_bldsk.png b/arknights_mower/resources/agent_name/char_171_bldsk.png
new file mode 100644
index 000000000..1703ed9a6
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_171_bldsk.png differ
diff --git a/arknights_mower/resources/agent_name/char_172_svrash.png b/arknights_mower/resources/agent_name/char_172_svrash.png
new file mode 100644
index 000000000..c7352ecb4
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_172_svrash.png differ
diff --git a/arknights_mower/resources/agent_name/char_173_slchan.png b/arknights_mower/resources/agent_name/char_173_slchan.png
new file mode 100644
index 000000000..229b1df5d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_173_slchan.png differ
diff --git a/arknights_mower/resources/agent_name/char_174_slbell.png b/arknights_mower/resources/agent_name/char_174_slbell.png
new file mode 100644
index 000000000..984d07223
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_174_slbell.png differ
diff --git a/arknights_mower/resources/agent_name/char_179_cgbird.png b/arknights_mower/resources/agent_name/char_179_cgbird.png
new file mode 100644
index 000000000..0353cddc9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_179_cgbird.png differ
diff --git a/arknights_mower/resources/agent_name/char_181_flower.png b/arknights_mower/resources/agent_name/char_181_flower.png
new file mode 100644
index 000000000..01803d680
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_181_flower.png differ
diff --git a/arknights_mower/resources/agent_name/char_183_skgoat.png b/arknights_mower/resources/agent_name/char_183_skgoat.png
new file mode 100644
index 000000000..48b47750f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_183_skgoat.png differ
diff --git a/arknights_mower/resources/agent_name/char_185_frncat.png b/arknights_mower/resources/agent_name/char_185_frncat.png
new file mode 100644
index 000000000..20cb22400
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_185_frncat.png differ
diff --git a/arknights_mower/resources/agent_name/char_188_helage.png b/arknights_mower/resources/agent_name/char_188_helage.png
new file mode 100644
index 000000000..f123ea112
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_188_helage.png differ
diff --git a/arknights_mower/resources/agent_name/char_190_clour.png b/arknights_mower/resources/agent_name/char_190_clour.png
new file mode 100644
index 000000000..a7e589e4d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_190_clour.png differ
diff --git a/arknights_mower/resources/agent_name/char_192_falco.png b/arknights_mower/resources/agent_name/char_192_falco.png
new file mode 100644
index 000000000..a970b469a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_192_falco.png differ
diff --git a/arknights_mower/resources/agent_name/char_193_frostl.png b/arknights_mower/resources/agent_name/char_193_frostl.png
new file mode 100644
index 000000000..6ce323c8a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_193_frostl.png differ
diff --git a/arknights_mower/resources/agent_name/char_195_glassb.png b/arknights_mower/resources/agent_name/char_195_glassb.png
new file mode 100644
index 000000000..8a6627a3e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_195_glassb.png differ
diff --git a/arknights_mower/resources/agent_name/char_196_sunbr.png b/arknights_mower/resources/agent_name/char_196_sunbr.png
new file mode 100644
index 000000000..2c0c097fd
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_196_sunbr.png differ
diff --git a/arknights_mower/resources/agent_name/char_197_poca.png b/arknights_mower/resources/agent_name/char_197_poca.png
new file mode 100644
index 000000000..5d0445765
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_197_poca.png differ
diff --git a/arknights_mower/resources/agent_name/char_199_yak.png b/arknights_mower/resources/agent_name/char_199_yak.png
new file mode 100644
index 000000000..02b93585a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_199_yak.png differ
diff --git a/arknights_mower/resources/agent_name/char_2013_cerber.png b/arknights_mower/resources/agent_name/char_2013_cerber.png
new file mode 100644
index 000000000..8def015db
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_2013_cerber.png differ
diff --git a/arknights_mower/resources/agent_name/char_201_moeshd.png b/arknights_mower/resources/agent_name/char_201_moeshd.png
new file mode 100644
index 000000000..71a1db4ee
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_201_moeshd.png differ
diff --git a/arknights_mower/resources/agent_name/char_202_demkni.png b/arknights_mower/resources/agent_name/char_202_demkni.png
new file mode 100644
index 000000000..6e1284e13
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_202_demkni.png differ
diff --git a/arknights_mower/resources/agent_name/char_204_platnm.png b/arknights_mower/resources/agent_name/char_204_platnm.png
new file mode 100644
index 000000000..869f74ca2
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_204_platnm.png differ
diff --git a/arknights_mower/resources/agent_name/char_208_melan.png b/arknights_mower/resources/agent_name/char_208_melan.png
new file mode 100644
index 000000000..b84aea6d2
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_208_melan.png differ
diff --git a/arknights_mower/resources/agent_name/char_210_stward.png b/arknights_mower/resources/agent_name/char_210_stward.png
new file mode 100644
index 000000000..966c22600
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_210_stward.png differ
diff --git a/arknights_mower/resources/agent_name/char_211_adnach.png b/arknights_mower/resources/agent_name/char_211_adnach.png
new file mode 100644
index 000000000..98c3331f9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_211_adnach.png differ
diff --git a/arknights_mower/resources/agent_name/char_212_ansel.png b/arknights_mower/resources/agent_name/char_212_ansel.png
new file mode 100644
index 000000000..d37047bb9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_212_ansel.png differ
diff --git a/arknights_mower/resources/agent_name/char_213_mostma.png b/arknights_mower/resources/agent_name/char_213_mostma.png
new file mode 100644
index 000000000..525e468b1
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_213_mostma.png differ
diff --git a/arknights_mower/resources/agent_name/char_215_mantic.png b/arknights_mower/resources/agent_name/char_215_mantic.png
new file mode 100644
index 000000000..0a28bb30a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_215_mantic.png differ
diff --git a/arknights_mower/resources/agent_name/char_218_cuttle.png b/arknights_mower/resources/agent_name/char_218_cuttle.png
new file mode 100644
index 000000000..3fada0e10
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_218_cuttle.png differ
diff --git a/arknights_mower/resources/agent_name/char_219_meteo.png b/arknights_mower/resources/agent_name/char_219_meteo.png
new file mode 100644
index 000000000..9ad99a805
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_219_meteo.png differ
diff --git a/arknights_mower/resources/agent_name/char_222_bpipe.png b/arknights_mower/resources/agent_name/char_222_bpipe.png
new file mode 100644
index 000000000..10f9458a5
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_222_bpipe.png differ
diff --git a/arknights_mower/resources/agent_name/char_225_haak.png b/arknights_mower/resources/agent_name/char_225_haak.png
new file mode 100644
index 000000000..c713fc7a7
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_225_haak.png differ
diff --git a/arknights_mower/resources/agent_name/char_226_hmau.png b/arknights_mower/resources/agent_name/char_226_hmau.png
new file mode 100644
index 000000000..facf7c385
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_226_hmau.png differ
diff --git a/arknights_mower/resources/agent_name/char_235_jesica.png b/arknights_mower/resources/agent_name/char_235_jesica.png
new file mode 100644
index 000000000..c77e32e2b
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_235_jesica.png differ
diff --git a/arknights_mower/resources/agent_name/char_236_rope.png b/arknights_mower/resources/agent_name/char_236_rope.png
new file mode 100644
index 000000000..405ce4457
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_236_rope.png differ
diff --git a/arknights_mower/resources/agent_name/char_237_gravel.png b/arknights_mower/resources/agent_name/char_237_gravel.png
new file mode 100644
index 000000000..a344512b4
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_237_gravel.png differ
diff --git a/arknights_mower/resources/agent_name/char_240_wyvern.png b/arknights_mower/resources/agent_name/char_240_wyvern.png
new file mode 100644
index 000000000..60ac1658d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_240_wyvern.png differ
diff --git a/arknights_mower/resources/agent_name/char_241_panda.png b/arknights_mower/resources/agent_name/char_241_panda.png
new file mode 100644
index 000000000..c2e49e1cb
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_241_panda.png differ
diff --git a/arknights_mower/resources/agent_name/char_242_otter.png b/arknights_mower/resources/agent_name/char_242_otter.png
new file mode 100644
index 000000000..429f232f8
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_242_otter.png differ
diff --git a/arknights_mower/resources/agent_name/char_243_waaifu.png b/arknights_mower/resources/agent_name/char_243_waaifu.png
new file mode 100644
index 000000000..d6f067a8d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_243_waaifu.png differ
diff --git a/arknights_mower/resources/agent_name/char_248_mgllan.png b/arknights_mower/resources/agent_name/char_248_mgllan.png
new file mode 100644
index 000000000..b71993641
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_248_mgllan.png differ
diff --git a/arknights_mower/resources/agent_name/char_250_phatom.png b/arknights_mower/resources/agent_name/char_250_phatom.png
new file mode 100644
index 000000000..066491a7f
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_250_phatom.png differ
diff --git a/arknights_mower/resources/agent_name/char_253_greyy.png b/arknights_mower/resources/agent_name/char_253_greyy.png
new file mode 100644
index 000000000..fad57944e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_253_greyy.png differ
diff --git a/arknights_mower/resources/agent_name/char_254_vodfox.png b/arknights_mower/resources/agent_name/char_254_vodfox.png
new file mode 100644
index 000000000..34d3b3a1a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_254_vodfox.png differ
diff --git a/arknights_mower/resources/agent_name/char_258_podego.png b/arknights_mower/resources/agent_name/char_258_podego.png
new file mode 100644
index 000000000..cd960bb9a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_258_podego.png differ
diff --git a/arknights_mower/resources/agent_name/char_261_sddrag.png b/arknights_mower/resources/agent_name/char_261_sddrag.png
new file mode 100644
index 000000000..0f516e045
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_261_sddrag.png differ
diff --git a/arknights_mower/resources/agent_name/char_263_skadi.png b/arknights_mower/resources/agent_name/char_263_skadi.png
new file mode 100644
index 000000000..22df84066
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_263_skadi.png differ
diff --git a/arknights_mower/resources/agent_name/char_271_spikes.png b/arknights_mower/resources/agent_name/char_271_spikes.png
new file mode 100644
index 000000000..079173b84
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_271_spikes.png differ
diff --git a/arknights_mower/resources/agent_name/char_272_strong.png b/arknights_mower/resources/agent_name/char_272_strong.png
new file mode 100644
index 000000000..527e0371a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_272_strong.png differ
diff --git a/arknights_mower/resources/agent_name/char_274_astesi.png b/arknights_mower/resources/agent_name/char_274_astesi.png
new file mode 100644
index 000000000..97bf5d5bd
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_274_astesi.png differ
diff --git a/arknights_mower/resources/agent_name/char_277_sqrrel.png b/arknights_mower/resources/agent_name/char_277_sqrrel.png
new file mode 100644
index 000000000..d571b2696
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_277_sqrrel.png differ
diff --git a/arknights_mower/resources/agent_name/char_278_orchid.png b/arknights_mower/resources/agent_name/char_278_orchid.png
new file mode 100644
index 000000000..48d52b8d5
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_278_orchid.png differ
diff --git a/arknights_mower/resources/agent_name/char_279_excu.png b/arknights_mower/resources/agent_name/char_279_excu.png
new file mode 100644
index 000000000..2021cf408
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_279_excu.png differ
diff --git a/arknights_mower/resources/agent_name/char_281_popka.png b/arknights_mower/resources/agent_name/char_281_popka.png
new file mode 100644
index 000000000..d947640d0
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_281_popka.png differ
diff --git a/arknights_mower/resources/agent_name/char_282_catap.png b/arknights_mower/resources/agent_name/char_282_catap.png
new file mode 100644
index 000000000..8846e5158
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_282_catap.png differ
diff --git a/arknights_mower/resources/agent_name/char_283_midn.png b/arknights_mower/resources/agent_name/char_283_midn.png
new file mode 100644
index 000000000..5de595545
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_283_midn.png differ
diff --git a/arknights_mower/resources/agent_name/char_284_spot.png b/arknights_mower/resources/agent_name/char_284_spot.png
new file mode 100644
index 000000000..68d97197e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_284_spot.png differ
diff --git a/arknights_mower/resources/agent_name/char_285_medic2.png b/arknights_mower/resources/agent_name/char_285_medic2.png
new file mode 100644
index 000000000..f13813717
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_285_medic2.png differ
diff --git a/arknights_mower/resources/agent_name/char_286_cast3.png b/arknights_mower/resources/agent_name/char_286_cast3.png
new file mode 100644
index 000000000..40ff9cc04
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_286_cast3.png differ
diff --git a/arknights_mower/resources/agent_name/char_289_gyuki.png b/arknights_mower/resources/agent_name/char_289_gyuki.png
new file mode 100644
index 000000000..92e8bda0e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_289_gyuki.png differ
diff --git a/arknights_mower/resources/agent_name/char_290_vigna.png b/arknights_mower/resources/agent_name/char_290_vigna.png
new file mode 100644
index 000000000..61f1cc47c
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_290_vigna.png differ
diff --git a/arknights_mower/resources/agent_name/char_293_thorns.png b/arknights_mower/resources/agent_name/char_293_thorns.png
new file mode 100644
index 000000000..fc8d327a3
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_293_thorns.png differ
diff --git a/arknights_mower/resources/agent_name/char_294_ayer.png b/arknights_mower/resources/agent_name/char_294_ayer.png
new file mode 100644
index 000000000..e240b9579
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_294_ayer.png differ
diff --git a/arknights_mower/resources/agent_name/char_298_susuro.png b/arknights_mower/resources/agent_name/char_298_susuro.png
new file mode 100644
index 000000000..e780618b3
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_298_susuro.png differ
diff --git a/arknights_mower/resources/agent_name/char_301_cutter.png b/arknights_mower/resources/agent_name/char_301_cutter.png
new file mode 100644
index 000000000..2d094d53b
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_301_cutter.png differ
diff --git a/arknights_mower/resources/agent_name/char_302_glaze.png b/arknights_mower/resources/agent_name/char_302_glaze.png
new file mode 100644
index 000000000..bcd0bbd00
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_302_glaze.png differ
diff --git a/arknights_mower/resources/agent_name/char_306_leizi.png b/arknights_mower/resources/agent_name/char_306_leizi.png
new file mode 100644
index 000000000..bdea4fbbd
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_306_leizi.png differ
diff --git a/arknights_mower/resources/agent_name/char_308_swire.png b/arknights_mower/resources/agent_name/char_308_swire.png
new file mode 100644
index 000000000..ba3767161
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_308_swire.png differ
diff --git a/arknights_mower/resources/agent_name/char_326_glacus.png b/arknights_mower/resources/agent_name/char_326_glacus.png
new file mode 100644
index 000000000..4ac4cb2bb
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_326_glacus.png differ
diff --git a/arknights_mower/resources/agent_name/char_328_cammou.png b/arknights_mower/resources/agent_name/char_328_cammou.png
new file mode 100644
index 000000000..5b828c4cc
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_328_cammou.png differ
diff --git a/arknights_mower/resources/agent_name/char_337_utage.png b/arknights_mower/resources/agent_name/char_337_utage.png
new file mode 100644
index 000000000..5bc6bd3c5
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_337_utage.png differ
diff --git a/arknights_mower/resources/agent_name/char_340_shwaz.png b/arknights_mower/resources/agent_name/char_340_shwaz.png
new file mode 100644
index 000000000..6b9e3fcd9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_340_shwaz.png differ
diff --git a/arknights_mower/resources/agent_name/char_343_tknogi.png b/arknights_mower/resources/agent_name/char_343_tknogi.png
new file mode 100644
index 000000000..bae63351b
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_343_tknogi.png differ
diff --git a/arknights_mower/resources/agent_name/char_344_beewax.png b/arknights_mower/resources/agent_name/char_344_beewax.png
new file mode 100644
index 000000000..e075c32be
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_344_beewax.png differ
diff --git a/arknights_mower/resources/agent_name/char_346_aosta.png b/arknights_mower/resources/agent_name/char_346_aosta.png
new file mode 100644
index 000000000..2956658d0
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_346_aosta.png differ
diff --git a/arknights_mower/resources/agent_name/char_349_chiave.png b/arknights_mower/resources/agent_name/char_349_chiave.png
new file mode 100644
index 000000000..980bf51d8
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_349_chiave.png differ
diff --git a/arknights_mower/resources/agent_name/char_350_surtr.png b/arknights_mower/resources/agent_name/char_350_surtr.png
new file mode 100644
index 000000000..b515e5435
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_350_surtr.png differ
diff --git a/arknights_mower/resources/agent_name/char_356_broca.png b/arknights_mower/resources/agent_name/char_356_broca.png
new file mode 100644
index 000000000..654e164c1
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_356_broca.png differ
diff --git a/arknights_mower/resources/agent_name/char_358_lisa.png b/arknights_mower/resources/agent_name/char_358_lisa.png
new file mode 100644
index 000000000..127cdf18e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_358_lisa.png differ
diff --git a/arknights_mower/resources/agent_name/char_365_aprl.png b/arknights_mower/resources/agent_name/char_365_aprl.png
new file mode 100644
index 000000000..b8ac689ae
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_365_aprl.png differ
diff --git a/arknights_mower/resources/agent_name/char_366_acdrop.png b/arknights_mower/resources/agent_name/char_366_acdrop.png
new file mode 100644
index 000000000..6be3d86bb
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_366_acdrop.png differ
diff --git a/arknights_mower/resources/agent_name/char_367_swllow.png b/arknights_mower/resources/agent_name/char_367_swllow.png
new file mode 100644
index 000000000..ddb4bc63c
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_367_swllow.png differ
diff --git a/arknights_mower/resources/agent_name/char_373_lionhd.png b/arknights_mower/resources/agent_name/char_373_lionhd.png
new file mode 100644
index 000000000..ea53e109a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_373_lionhd.png differ
diff --git a/arknights_mower/resources/agent_name/char_376_therex.png b/arknights_mower/resources/agent_name/char_376_therex.png
new file mode 100644
index 000000000..691c35e94
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_376_therex.png differ
diff --git a/arknights_mower/resources/agent_name/char_378_asbest.png b/arknights_mower/resources/agent_name/char_378_asbest.png
new file mode 100644
index 000000000..d9c54988d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_378_asbest.png differ
diff --git a/arknights_mower/resources/agent_name/char_379_sesa.png b/arknights_mower/resources/agent_name/char_379_sesa.png
new file mode 100644
index 000000000..df11dbf4a
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_379_sesa.png differ
diff --git a/arknights_mower/resources/agent_name/char_381_bubble.png b/arknights_mower/resources/agent_name/char_381_bubble.png
new file mode 100644
index 000000000..3412f7fd6
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_381_bubble.png differ
diff --git a/arknights_mower/resources/agent_name/char_385_finlpp.png b/arknights_mower/resources/agent_name/char_385_finlpp.png
new file mode 100644
index 000000000..1f89b4b58
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_385_finlpp.png differ
diff --git a/arknights_mower/resources/agent_name/char_4000_jnight.png b/arknights_mower/resources/agent_name/char_4000_jnight.png
new file mode 100644
index 000000000..a1028f55b
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_4000_jnight.png differ
diff --git a/arknights_mower/resources/agent_name/char_400_weedy.png b/arknights_mower/resources/agent_name/char_400_weedy.png
new file mode 100644
index 000000000..83219b3ff
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_400_weedy.png differ
diff --git a/arknights_mower/resources/agent_name/char_401_elysm.png b/arknights_mower/resources/agent_name/char_401_elysm.png
new file mode 100644
index 000000000..4261c1f8d
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_401_elysm.png differ
diff --git a/arknights_mower/resources/agent_name/char_4093_frston.png b/arknights_mower/resources/agent_name/char_4093_frston.png
new file mode 100644
index 000000000..ff54dbba9
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_4093_frston.png differ
diff --git a/arknights_mower/resources/agent_name/char_4136_phonor.png b/arknights_mower/resources/agent_name/char_4136_phonor.png
new file mode 100644
index 000000000..a1a88ffcf
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_4136_phonor.png differ
diff --git a/arknights_mower/resources/agent_name/char_415_flint.png b/arknights_mower/resources/agent_name/char_415_flint.png
new file mode 100644
index 000000000..adcc27b17
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_415_flint.png differ
diff --git a/arknights_mower/resources/agent_name/char_416_zumama.png b/arknights_mower/resources/agent_name/char_416_zumama.png
new file mode 100644
index 000000000..d062876e3
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_416_zumama.png differ
diff --git a/arknights_mower/resources/agent_name/char_423_blemsh.png b/arknights_mower/resources/agent_name/char_423_blemsh.png
new file mode 100644
index 000000000..4f6aa8c3e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_423_blemsh.png differ
diff --git a/arknights_mower/resources/agent_name/char_500_noirc.png b/arknights_mower/resources/agent_name/char_500_noirc.png
new file mode 100644
index 000000000..44316710e
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_500_noirc.png differ
diff --git a/arknights_mower/resources/agent_name/char_501_durin.png b/arknights_mower/resources/agent_name/char_501_durin.png
new file mode 100644
index 000000000..72e92e270
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_501_durin.png differ
diff --git a/arknights_mower/resources/agent_name/char_502_nblade.png b/arknights_mower/resources/agent_name/char_502_nblade.png
new file mode 100644
index 000000000..29bf653c4
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_502_nblade.png differ
diff --git a/arknights_mower/resources/agent_name/char_503_rang.png b/arknights_mower/resources/agent_name/char_503_rang.png
new file mode 100644
index 000000000..d3b077532
Binary files /dev/null and b/arknights_mower/resources/agent_name/char_503_rang.png differ
diff --git a/arknights_mower/resources/agent_on_shift.png b/arknights_mower/resources/agent_on_shift.png
deleted file mode 100644
index d9f3d7c62..000000000
Binary files a/arknights_mower/resources/agent_on_shift.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_resting.png b/arknights_mower/resources/agent_resting.png
deleted file mode 100644
index 9f7e8029e..000000000
Binary files a/arknights_mower/resources/agent_resting.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_token.png b/arknights_mower/resources/agent_token.png
deleted file mode 100644
index 59ddddc7c..000000000
Binary files a/arknights_mower/resources/agent_token.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_token_1080_1440.png b/arknights_mower/resources/agent_token_1080_1440.png
deleted file mode 100644
index 27591c9dd..000000000
Binary files a/arknights_mower/resources/agent_token_1080_1440.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_token_900_1440.png b/arknights_mower/resources/agent_token_900_1440.png
deleted file mode 100644
index 1556f090f..000000000
Binary files a/arknights_mower/resources/agent_token_900_1440.png and /dev/null differ
diff --git a/arknights_mower/resources/agent_unlock.png b/arknights_mower/resources/agent_unlock.png
deleted file mode 100644
index f8fc2e43e..000000000
Binary files a/arknights_mower/resources/agent_unlock.png and /dev/null differ
diff --git a/arknights_mower/resources/announcement_close.png b/arknights_mower/resources/announcement_close.png
new file mode 100644
index 000000000..8ab94cc1f
Binary files /dev/null and b/arknights_mower/resources/announcement_close.png differ
diff --git a/arknights_mower/resources/arrange_blue_yes.png b/arknights_mower/resources/arrange_blue_yes.png
index aaa07235b..be09e18b3 100644
Binary files a/arknights_mower/resources/arrange_blue_yes.png and b/arknights_mower/resources/arrange_blue_yes.png differ
diff --git a/arknights_mower/resources/arrange_check_in.png b/arknights_mower/resources/arrange_check_in.png
index 39f787295..754fd532a 100644
Binary files a/arknights_mower/resources/arrange_check_in.png and b/arknights_mower/resources/arrange_check_in.png differ
diff --git a/arknights_mower/resources/arrange_check_in_on.png b/arknights_mower/resources/arrange_check_in_on.png
index 35de882cf..69ece57df 100644
Binary files a/arknights_mower/resources/arrange_check_in_on.png and b/arknights_mower/resources/arrange_check_in_on.png differ
diff --git a/arknights_mower/resources/arrange_clean.png b/arknights_mower/resources/arrange_clean.png
deleted file mode 100644
index 876cbf6ff..000000000
Binary files a/arknights_mower/resources/arrange_clean.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_empty_room.png b/arknights_mower/resources/arrange_empty_room.png
deleted file mode 100644
index cd561ff1d..000000000
Binary files a/arknights_mower/resources/arrange_empty_room.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_feeling.png b/arknights_mower/resources/arrange_feeling.png
deleted file mode 100644
index dbfb52248..000000000
Binary files a/arknights_mower/resources/arrange_feeling.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_non_check_in.png b/arknights_mower/resources/arrange_non_check_in.png
deleted file mode 100644
index e498573e2..000000000
Binary files a/arknights_mower/resources/arrange_non_check_in.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_order_options.png b/arknights_mower/resources/arrange_order_options.png
index 28edc756b..ca81c4d05 100644
Binary files a/arknights_mower/resources/arrange_order_options.png and b/arknights_mower/resources/arrange_order_options.png differ
diff --git a/arknights_mower/resources/arrange_order_options_scene.png b/arknights_mower/resources/arrange_order_options_scene.png
index b11fec657..c9ce07224 100644
Binary files a/arknights_mower/resources/arrange_order_options_scene.png and b/arknights_mower/resources/arrange_order_options_scene.png differ
diff --git a/arknights_mower/resources/arrange_skill.png b/arknights_mower/resources/arrange_skill.png
deleted file mode 100644
index 041b9ac4c..000000000
Binary files a/arknights_mower/resources/arrange_skill.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_status.png b/arknights_mower/resources/arrange_status.png
deleted file mode 100644
index 3b84dcef7..000000000
Binary files a/arknights_mower/resources/arrange_status.png and /dev/null differ
diff --git a/arknights_mower/resources/arrange_trust.png b/arknights_mower/resources/arrange_trust.png
deleted file mode 100644
index 2fea8daaf..000000000
Binary files a/arknights_mower/resources/arrange_trust.png and /dev/null differ
diff --git a/arknights_mower/resources/business_card.png b/arknights_mower/resources/business_card.png
new file mode 100644
index 000000000..c4d171413
Binary files /dev/null and b/arknights_mower/resources/business_card.png differ
diff --git a/arknights_mower/resources/cadpa_detail.png b/arknights_mower/resources/cadpa_detail.png
deleted file mode 100644
index f75d7015b..000000000
Binary files a/arknights_mower/resources/cadpa_detail.png and /dev/null differ
diff --git a/arknights_mower/resources/close_mine.png b/arknights_mower/resources/close_mine.png
deleted file mode 100644
index 685e3d3db..000000000
Binary files a/arknights_mower/resources/close_mine.png and /dev/null differ
diff --git a/arknights_mower/resources/clue/1.png b/arknights_mower/resources/clue/1.png
new file mode 100644
index 000000000..6db75fd3e
Binary files /dev/null and b/arknights_mower/resources/clue/1.png differ
diff --git a/arknights_mower/resources/clue/2.png b/arknights_mower/resources/clue/2.png
new file mode 100644
index 000000000..153b64687
Binary files /dev/null and b/arknights_mower/resources/clue/2.png differ
diff --git a/arknights_mower/resources/clue/3.png b/arknights_mower/resources/clue/3.png
new file mode 100644
index 000000000..52c28d133
Binary files /dev/null and b/arknights_mower/resources/clue/3.png differ
diff --git a/arknights_mower/resources/clue/4.png b/arknights_mower/resources/clue/4.png
new file mode 100644
index 000000000..fae3df55d
Binary files /dev/null and b/arknights_mower/resources/clue/4.png differ
diff --git a/arknights_mower/resources/clue/5.png b/arknights_mower/resources/clue/5.png
new file mode 100644
index 000000000..e7881916f
Binary files /dev/null and b/arknights_mower/resources/clue/5.png differ
diff --git a/arknights_mower/resources/clue/6.png b/arknights_mower/resources/clue/6.png
new file mode 100644
index 000000000..adbb8080b
Binary files /dev/null and b/arknights_mower/resources/clue/6.png differ
diff --git a/arknights_mower/resources/clue/7.png b/arknights_mower/resources/clue/7.png
new file mode 100644
index 000000000..ab0b664fd
Binary files /dev/null and b/arknights_mower/resources/clue/7.png differ
diff --git a/arknights_mower/resources/clue/badge_new.png b/arknights_mower/resources/clue/badge_new.png
new file mode 100644
index 000000000..dc5e54e74
Binary files /dev/null and b/arknights_mower/resources/clue/badge_new.png differ
diff --git a/arknights_mower/resources/clue/button_get.png b/arknights_mower/resources/clue/button_get.png
new file mode 100644
index 000000000..76af9da42
Binary files /dev/null and b/arknights_mower/resources/clue/button_get.png differ
diff --git a/arknights_mower/resources/clue/button_unlock.png b/arknights_mower/resources/clue/button_unlock.png
new file mode 100644
index 000000000..d68dc39af
Binary files /dev/null and b/arknights_mower/resources/clue/button_unlock.png differ
diff --git a/arknights_mower/resources/clue/daily.png b/arknights_mower/resources/clue/daily.png
new file mode 100644
index 000000000..abb28a5f4
Binary files /dev/null and b/arknights_mower/resources/clue/daily.png differ
diff --git a/arknights_mower/resources/clue/filter_all.png b/arknights_mower/resources/clue/filter_all.png
new file mode 100644
index 000000000..8858b0cef
Binary files /dev/null and b/arknights_mower/resources/clue/filter_all.png differ
diff --git a/arknights_mower/resources/clue/give_away.png b/arknights_mower/resources/clue/give_away.png
new file mode 100644
index 000000000..19812ecb5
Binary files /dev/null and b/arknights_mower/resources/clue/give_away.png differ
diff --git a/arknights_mower/resources/clue/icon_notification.png b/arknights_mower/resources/clue/icon_notification.png
new file mode 100644
index 000000000..5fdeba962
Binary files /dev/null and b/arknights_mower/resources/clue/icon_notification.png differ
diff --git a/arknights_mower/resources/clue/label_give_away.png b/arknights_mower/resources/clue/label_give_away.png
new file mode 100644
index 000000000..822aeef14
Binary files /dev/null and b/arknights_mower/resources/clue/label_give_away.png differ
diff --git a/arknights_mower/resources/clue/receive.png b/arknights_mower/resources/clue/receive.png
new file mode 100644
index 000000000..c70527ae9
Binary files /dev/null and b/arknights_mower/resources/clue/receive.png differ
diff --git a/arknights_mower/resources/clue/summary.png b/arknights_mower/resources/clue/summary.png
new file mode 100644
index 000000000..066a4e0a0
Binary files /dev/null and b/arknights_mower/resources/clue/summary.png differ
diff --git a/arknights_mower/resources/clue/title_party.png b/arknights_mower/resources/clue/title_party.png
new file mode 100644
index 000000000..935d539f2
Binary files /dev/null and b/arknights_mower/resources/clue/title_party.png differ
diff --git a/arknights_mower/resources/clue_full.png b/arknights_mower/resources/clue_full.png
deleted file mode 100644
index 86e037f98..000000000
Binary files a/arknights_mower/resources/clue_full.png and /dev/null differ
diff --git a/arknights_mower/resources/clue_func.png b/arknights_mower/resources/clue_func.png
deleted file mode 100644
index 393551c4c..000000000
Binary files a/arknights_mower/resources/clue_func.png and /dev/null differ
diff --git a/arknights_mower/resources/clue_nav.png b/arknights_mower/resources/clue_nav.png
deleted file mode 100644
index 773465e7d..000000000
Binary files a/arknights_mower/resources/clue_nav.png and /dev/null differ
diff --git a/arknights_mower/resources/clue_next.png b/arknights_mower/resources/clue_next.png
new file mode 100644
index 000000000..c1a7146e2
Binary files /dev/null and b/arknights_mower/resources/clue_next.png differ
diff --git a/arknights_mower/resources/clue_obtain.png b/arknights_mower/resources/clue_obtain.png
deleted file mode 100644
index 972e14bff..000000000
Binary files a/arknights_mower/resources/clue_obtain.png and /dev/null differ
diff --git a/arknights_mower/resources/clue_summary.png b/arknights_mower/resources/clue_summary.png
deleted file mode 100644
index dc5ae607d..000000000
Binary files a/arknights_mower/resources/clue_summary.png and /dev/null differ
diff --git a/arknights_mower/resources/clue_unlock.png b/arknights_mower/resources/clue_unlock.png
deleted file mode 100644
index d0e8dbca9..000000000
Binary files a/arknights_mower/resources/clue_unlock.png and /dev/null differ
diff --git a/arknights_mower/resources/collection_small.png b/arknights_mower/resources/collection_small.png
new file mode 100644
index 000000000..d76702d73
Binary files /dev/null and b/arknights_mower/resources/collection_small.png differ
diff --git a/arknights_mower/resources/confirm.png b/arknights_mower/resources/confirm.png
new file mode 100644
index 000000000..aaaef4ef8
Binary files /dev/null and b/arknights_mower/resources/confirm.png differ
diff --git a/arknights_mower/resources/confirm_blue.png b/arknights_mower/resources/confirm_blue.png
index 3d18c9b4e..007aa3e2c 100644
Binary files a/arknights_mower/resources/confirm_blue.png and b/arknights_mower/resources/confirm_blue.png differ
diff --git a/arknights_mower/resources/control_central_assistants.png b/arknights_mower/resources/control_central_assistants.png
new file mode 100644
index 000000000..7f9ea0bfc
Binary files /dev/null and b/arknights_mower/resources/control_central_assistants.png differ
diff --git a/arknights_mower/resources/credit_shop_countdown.png b/arknights_mower/resources/credit_shop_countdown.png
new file mode 100644
index 000000000..5753722b8
Binary files /dev/null and b/arknights_mower/resources/credit_shop_countdown.png differ
diff --git a/arknights_mower/resources/depot.png b/arknights_mower/resources/depot.png
new file mode 100644
index 000000000..3384893d0
Binary files /dev/null and b/arknights_mower/resources/depot.png differ
diff --git a/arknights_mower/resources/depot_empty.png b/arknights_mower/resources/depot_empty.png
new file mode 100644
index 000000000..7dc87cb9d
Binary files /dev/null and b/arknights_mower/resources/depot_empty.png differ
diff --git a/arknights_mower/resources/depot_num/digit_0.png b/arknights_mower/resources/depot_num/digit_0.png
new file mode 100644
index 000000000..9d44183a1
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_0.png differ
diff --git a/arknights_mower/resources/depot_num/digit_1.png b/arknights_mower/resources/depot_num/digit_1.png
new file mode 100644
index 000000000..6fd8ae305
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_1.png differ
diff --git a/arknights_mower/resources/depot_num/digit_2.png b/arknights_mower/resources/depot_num/digit_2.png
new file mode 100644
index 000000000..74f56db34
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_2.png differ
diff --git a/arknights_mower/resources/depot_num/digit_3.png b/arknights_mower/resources/depot_num/digit_3.png
new file mode 100644
index 000000000..577f47314
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_3.png differ
diff --git a/arknights_mower/resources/depot_num/digit_4.png b/arknights_mower/resources/depot_num/digit_4.png
new file mode 100644
index 000000000..53e14a480
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_4.png differ
diff --git a/arknights_mower/resources/depot_num/digit_5.png b/arknights_mower/resources/depot_num/digit_5.png
new file mode 100644
index 000000000..9f6644528
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_5.png differ
diff --git a/arknights_mower/resources/depot_num/digit_6.png b/arknights_mower/resources/depot_num/digit_6.png
new file mode 100644
index 000000000..9821f152c
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_6.png differ
diff --git a/arknights_mower/resources/depot_num/digit_7.png b/arknights_mower/resources/depot_num/digit_7.png
new file mode 100644
index 000000000..2b66e5293
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_7.png differ
diff --git a/arknights_mower/resources/depot_num/digit_8.png b/arknights_mower/resources/depot_num/digit_8.png
new file mode 100644
index 000000000..68837e04d
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_8.png differ
diff --git a/arknights_mower/resources/depot_num/digit_9.png b/arknights_mower/resources/depot_num/digit_9.png
new file mode 100644
index 000000000..e03c7d213
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_9.png differ
diff --git a/arknights_mower/resources/depot_num/digit_91.png b/arknights_mower/resources/depot_num/digit_91.png
new file mode 100644
index 000000000..3744de9f5
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_91.png differ
diff --git a/arknights_mower/resources/depot_num/digit_point.png b/arknights_mower/resources/depot_num/digit_point.png
new file mode 100644
index 000000000..eb462ac2f
Binary files /dev/null and b/arknights_mower/resources/depot_num/digit_point.png differ
diff --git a/arknights_mower/resources/distracted.png b/arknights_mower/resources/distracted.png
deleted file mode 100644
index 291228118..000000000
Binary files a/arknights_mower/resources/distracted.png and /dev/null differ
diff --git a/arknights_mower/resources/double_confirm.png b/arknights_mower/resources/double_confirm.png
deleted file mode 100644
index 4de6c4933..000000000
Binary files a/arknights_mower/resources/double_confirm.png and /dev/null differ
diff --git a/arknights_mower/resources/double_confirm/exit.png b/arknights_mower/resources/double_confirm/exit.png
new file mode 100644
index 000000000..f8cbe103a
Binary files /dev/null and b/arknights_mower/resources/double_confirm/exit.png differ
diff --git a/arknights_mower/resources/double_confirm/friend.png b/arknights_mower/resources/double_confirm/friend.png
new file mode 100644
index 000000000..beda8e5dc
Binary files /dev/null and b/arknights_mower/resources/double_confirm/friend.png differ
diff --git a/arknights_mower/resources/double_confirm/give_up.png b/arknights_mower/resources/double_confirm/give_up.png
new file mode 100644
index 000000000..b7755b570
Binary files /dev/null and b/arknights_mower/resources/double_confirm/give_up.png differ
diff --git a/arknights_mower/resources/double_confirm/infrastructure.png b/arknights_mower/resources/double_confirm/infrastructure.png
new file mode 100644
index 000000000..f06b4afc1
Binary files /dev/null and b/arknights_mower/resources/double_confirm/infrastructure.png differ
diff --git a/arknights_mower/resources/double_confirm/main.png b/arknights_mower/resources/double_confirm/main.png
new file mode 100644
index 000000000..a9843a17e
Binary files /dev/null and b/arknights_mower/resources/double_confirm/main.png differ
diff --git a/arknights_mower/resources/double_confirm/network.png b/arknights_mower/resources/double_confirm/network.png
new file mode 100644
index 000000000..133ccfbdc
Binary files /dev/null and b/arknights_mower/resources/double_confirm/network.png differ
diff --git a/arknights_mower/resources/double_confirm/recruit.png b/arknights_mower/resources/double_confirm/recruit.png
new file mode 100644
index 000000000..12a4697ae
Binary files /dev/null and b/arknights_mower/resources/double_confirm/recruit.png differ
diff --git a/arknights_mower/resources/double_confirm/voice.png b/arknights_mower/resources/double_confirm/voice.png
new file mode 100644
index 000000000..fa6694a41
Binary files /dev/null and b/arknights_mower/resources/double_confirm/voice.png differ
diff --git a/arknights_mower/resources/drone.png b/arknights_mower/resources/drone.png
new file mode 100644
index 000000000..017e10c44
Binary files /dev/null and b/arknights_mower/resources/drone.png differ
diff --git a/arknights_mower/resources/drone_count/0.png b/arknights_mower/resources/drone_count/0.png
new file mode 100644
index 000000000..7d1810a2f
Binary files /dev/null and b/arknights_mower/resources/drone_count/0.png differ
diff --git a/arknights_mower/resources/drone_count/1.png b/arknights_mower/resources/drone_count/1.png
new file mode 100644
index 000000000..a85e6f058
Binary files /dev/null and b/arknights_mower/resources/drone_count/1.png differ
diff --git a/arknights_mower/resources/drone_count/2.png b/arknights_mower/resources/drone_count/2.png
new file mode 100644
index 000000000..734a9eef9
Binary files /dev/null and b/arknights_mower/resources/drone_count/2.png differ
diff --git a/arknights_mower/resources/drone_count/3.png b/arknights_mower/resources/drone_count/3.png
new file mode 100644
index 000000000..06c960001
Binary files /dev/null and b/arknights_mower/resources/drone_count/3.png differ
diff --git a/arknights_mower/resources/drone_count/4.png b/arknights_mower/resources/drone_count/4.png
new file mode 100644
index 000000000..cc922c4bf
Binary files /dev/null and b/arknights_mower/resources/drone_count/4.png differ
diff --git a/arknights_mower/resources/drone_count/5.png b/arknights_mower/resources/drone_count/5.png
new file mode 100644
index 000000000..e549427c0
Binary files /dev/null and b/arknights_mower/resources/drone_count/5.png differ
diff --git a/arknights_mower/resources/drone_count/6.png b/arknights_mower/resources/drone_count/6.png
new file mode 100644
index 000000000..2fae56c72
Binary files /dev/null and b/arknights_mower/resources/drone_count/6.png differ
diff --git a/arknights_mower/resources/drone_count/7.png b/arknights_mower/resources/drone_count/7.png
new file mode 100644
index 000000000..b67e3b321
Binary files /dev/null and b/arknights_mower/resources/drone_count/7.png differ
diff --git a/arknights_mower/resources/drone_count/8.png b/arknights_mower/resources/drone_count/8.png
new file mode 100644
index 000000000..a3e2581ee
Binary files /dev/null and b/arknights_mower/resources/drone_count/8.png differ
diff --git a/arknights_mower/resources/drone_count/9.png b/arknights_mower/resources/drone_count/9.png
new file mode 100644
index 000000000..ae1bd00a7
Binary files /dev/null and b/arknights_mower/resources/drone_count/9.png differ
diff --git a/arknights_mower/resources/factory_collect.png b/arknights_mower/resources/factory_collect.png
new file mode 100644
index 000000000..70df22136
Binary files /dev/null and b/arknights_mower/resources/factory_collect.png differ
diff --git a/arknights_mower/resources/fia_full.png b/arknights_mower/resources/fia_full.png
deleted file mode 100644
index 1de043956..000000000
Binary files a/arknights_mower/resources/fia_full.png and /dev/null differ
diff --git a/arknights_mower/resources/fia_full_elite2.png b/arknights_mower/resources/fia_full_elite2.png
deleted file mode 100644
index 5ab5accf0..000000000
Binary files a/arknights_mower/resources/fia_full_elite2.png and /dev/null differ
diff --git a/arknights_mower/resources/fia_resting.png b/arknights_mower/resources/fia_resting.png
deleted file mode 100644
index 20b5eed06..000000000
Binary files a/arknights_mower/resources/fia_resting.png and /dev/null differ
diff --git a/arknights_mower/resources/fia_resting_elite2.png b/arknights_mower/resources/fia_resting_elite2.png
deleted file mode 100644
index 93e4aca93..000000000
Binary files a/arknights_mower/resources/fia_resting_elite2.png and /dev/null differ
diff --git a/arknights_mower/resources/fight/c.png b/arknights_mower/resources/fight/c.png
new file mode 100644
index 000000000..b214d03ff
Binary files /dev/null and b/arknights_mower/resources/fight/c.png differ
diff --git a/arknights_mower/resources/fight/c_mask.png b/arknights_mower/resources/fight/c_mask.png
new file mode 100644
index 000000000..0e471a428
Binary files /dev/null and b/arknights_mower/resources/fight/c_mask.png differ
diff --git a/arknights_mower/resources/fight/choose.png b/arknights_mower/resources/fight/choose.png
new file mode 100644
index 000000000..a25523ee9
Binary files /dev/null and b/arknights_mower/resources/fight/choose.png differ
diff --git a/arknights_mower/resources/fight/complete.png b/arknights_mower/resources/fight/complete.png
new file mode 100644
index 000000000..e0dc4f2fa
Binary files /dev/null and b/arknights_mower/resources/fight/complete.png differ
diff --git a/arknights_mower/resources/fight/enemy.png b/arknights_mower/resources/fight/enemy.png
new file mode 100644
index 000000000..b3e70400d
Binary files /dev/null and b/arknights_mower/resources/fight/enemy.png differ
diff --git a/arknights_mower/resources/fight/failed_text.png b/arknights_mower/resources/fight/failed_text.png
new file mode 100644
index 000000000..4b0981172
Binary files /dev/null and b/arknights_mower/resources/fight/failed_text.png differ
diff --git a/arknights_mower/resources/fight/kills_separator.png b/arknights_mower/resources/fight/kills_separator.png
new file mode 100644
index 000000000..14a7e634a
Binary files /dev/null and b/arknights_mower/resources/fight/kills_separator.png differ
diff --git a/arknights_mower/resources/fight/pause.png b/arknights_mower/resources/fight/pause.png
new file mode 100644
index 000000000..2f44f69d2
Binary files /dev/null and b/arknights_mower/resources/fight/pause.png differ
diff --git a/arknights_mower/resources/fight/refresh.png b/arknights_mower/resources/fight/refresh.png
new file mode 100644
index 000000000..c2b461cc4
Binary files /dev/null and b/arknights_mower/resources/fight/refresh.png differ
diff --git a/arknights_mower/resources/fight/skill_ready.png b/arknights_mower/resources/fight/skill_ready.png
new file mode 100644
index 000000000..74e30d773
Binary files /dev/null and b/arknights_mower/resources/fight/skill_ready.png differ
diff --git a/arknights_mower/resources/fight/use.png b/arknights_mower/resources/fight/use.png
new file mode 100644
index 000000000..f7f772ba4
Binary files /dev/null and b/arknights_mower/resources/fight/use.png differ
diff --git a/arknights_mower/resources/friend_list.png b/arknights_mower/resources/friend_list.png
index 9c13e4765..b4b0fb098 100644
Binary files a/arknights_mower/resources/friend_list.png and b/arknights_mower/resources/friend_list.png differ
diff --git a/arknights_mower/resources/friend_list_on.png b/arknights_mower/resources/friend_list_on.png
deleted file mode 100644
index 1a6d63479..000000000
Binary files a/arknights_mower/resources/friend_list_on.png and /dev/null differ
diff --git a/arknights_mower/resources/friend_visit.png b/arknights_mower/resources/friend_visit.png
index 70aa3e647..79b928d1d 100644
Binary files a/arknights_mower/resources/friend_visit.png and b/arknights_mower/resources/friend_visit.png differ
diff --git a/arknights_mower/resources/index_friend.png b/arknights_mower/resources/index_friend.png
deleted file mode 100644
index 197fdbb12..000000000
Binary files a/arknights_mower/resources/index_friend.png and /dev/null differ
diff --git a/arknights_mower/resources/index_infrastructure.png b/arknights_mower/resources/index_infrastructure.png
deleted file mode 100644
index 00f9e5dde..000000000
Binary files a/arknights_mower/resources/index_infrastructure.png and /dev/null differ
diff --git a/arknights_mower/resources/index_mission.png b/arknights_mower/resources/index_mission.png
deleted file mode 100644
index d3e400145..000000000
Binary files a/arknights_mower/resources/index_mission.png and /dev/null differ
diff --git a/arknights_mower/resources/index_recruit.png b/arknights_mower/resources/index_recruit.png
deleted file mode 100644
index 2b76ea163..000000000
Binary files a/arknights_mower/resources/index_recruit.png and /dev/null differ
diff --git a/arknights_mower/resources/index_shop.png b/arknights_mower/resources/index_shop.png
deleted file mode 100644
index e1a3564da..000000000
Binary files a/arknights_mower/resources/index_shop.png and /dev/null differ
diff --git a/arknights_mower/resources/index_terminal.png b/arknights_mower/resources/index_terminal.png
deleted file mode 100644
index fd8f7bca1..000000000
Binary files a/arknights_mower/resources/index_terminal.png and /dev/null differ
diff --git a/arknights_mower/resources/infra_collect_bill.png b/arknights_mower/resources/infra_collect_bill.png
index 1073d9d83..9567207b3 100644
Binary files a/arknights_mower/resources/infra_collect_bill.png and b/arknights_mower/resources/infra_collect_bill.png differ
diff --git a/arknights_mower/resources/infra_collect_bill_small.png b/arknights_mower/resources/infra_collect_bill_small.png
deleted file mode 100644
index f031dd4f2..000000000
Binary files a/arknights_mower/resources/infra_collect_bill_small.png and /dev/null differ
diff --git a/arknights_mower/resources/infra_collect_factory.png b/arknights_mower/resources/infra_collect_factory.png
index e6da20f25..cc2320cc8 100644
Binary files a/arknights_mower/resources/infra_collect_factory.png and b/arknights_mower/resources/infra_collect_factory.png differ
diff --git a/arknights_mower/resources/infra_collect_factory_small.png b/arknights_mower/resources/infra_collect_factory_small.png
deleted file mode 100644
index f253c89af..000000000
Binary files a/arknights_mower/resources/infra_collect_factory_small.png and /dev/null differ
diff --git a/arknights_mower/resources/infra_collect_trust.png b/arknights_mower/resources/infra_collect_trust.png
index dfc8f8f29..8243e9178 100644
Binary files a/arknights_mower/resources/infra_collect_trust.png and b/arknights_mower/resources/infra_collect_trust.png differ
diff --git a/arknights_mower/resources/infra_collect_trust_small.png b/arknights_mower/resources/infra_collect_trust_small.png
deleted file mode 100644
index aafc09100..000000000
Binary files a/arknights_mower/resources/infra_collect_trust_small.png and /dev/null differ
diff --git a/arknights_mower/resources/infra_exp_complete.png b/arknights_mower/resources/infra_exp_complete.png
new file mode 100644
index 000000000..0cd9bfef4
Binary files /dev/null and b/arknights_mower/resources/infra_exp_complete.png differ
diff --git a/arknights_mower/resources/infra_gold_complete.png b/arknights_mower/resources/infra_gold_complete.png
new file mode 100644
index 000000000..bd8fc3f13
Binary files /dev/null and b/arknights_mower/resources/infra_gold_complete.png differ
diff --git a/arknights_mower/resources/infra_lmd_complete.png b/arknights_mower/resources/infra_lmd_complete.png
new file mode 100644
index 000000000..151670586
Binary files /dev/null and b/arknights_mower/resources/infra_lmd_complete.png differ
diff --git a/arknights_mower/resources/infra_no_operator.png b/arknights_mower/resources/infra_no_operator.png
new file mode 100644
index 000000000..d6fe1045b
Binary files /dev/null and b/arknights_mower/resources/infra_no_operator.png differ
diff --git a/arknights_mower/resources/infra_ori_complete.png b/arknights_mower/resources/infra_ori_complete.png
new file mode 100644
index 000000000..bbfcb3a1b
Binary files /dev/null and b/arknights_mower/resources/infra_ori_complete.png differ
diff --git a/arknights_mower/resources/infra_oru_complete.png b/arknights_mower/resources/infra_oru_complete.png
new file mode 100644
index 000000000..1c114da60
Binary files /dev/null and b/arknights_mower/resources/infra_oru_complete.png differ
diff --git a/arknights_mower/resources/infra_overview_in.png b/arknights_mower/resources/infra_overview_in.png
index 2a6a8be9f..d5d8ea310 100644
Binary files a/arknights_mower/resources/infra_overview_in.png and b/arknights_mower/resources/infra_overview_in.png differ
diff --git a/arknights_mower/resources/infra_trust_complete.png b/arknights_mower/resources/infra_trust_complete.png
new file mode 100644
index 000000000..644e520e7
Binary files /dev/null and b/arknights_mower/resources/infra_trust_complete.png differ
diff --git a/arknights_mower/resources/loading.png b/arknights_mower/resources/loading.png
index 54e5e3762..714d4cfb1 100644
Binary files a/arknights_mower/resources/loading.png and b/arknights_mower/resources/loading.png differ
diff --git a/arknights_mower/resources/loading5.png b/arknights_mower/resources/loading5.png
deleted file mode 100644
index 5243f0839..000000000
Binary files a/arknights_mower/resources/loading5.png and /dev/null differ
diff --git a/arknights_mower/resources/loading6.png b/arknights_mower/resources/loading6.png
deleted file mode 100644
index 876db60ee..000000000
Binary files a/arknights_mower/resources/loading6.png and /dev/null differ
diff --git a/arknights_mower/resources/login_bilibili.png b/arknights_mower/resources/login_bilibili.png
index 440b9837b..57aa10fc4 100644
Binary files a/arknights_mower/resources/login_bilibili.png and b/arknights_mower/resources/login_bilibili.png differ
diff --git a/arknights_mower/resources/login_bilibili_entry.png b/arknights_mower/resources/login_bilibili_entry.png
deleted file mode 100644
index 135d6a698..000000000
Binary files a/arknights_mower/resources/login_bilibili_entry.png and /dev/null differ
diff --git a/arknights_mower/resources/login_bilibili_privacy.png b/arknights_mower/resources/login_bilibili_privacy.png
new file mode 100644
index 000000000..35e2e4669
Binary files /dev/null and b/arknights_mower/resources/login_bilibili_privacy.png differ
diff --git a/arknights_mower/resources/login_captcha.png b/arknights_mower/resources/login_captcha.png
index 1b4a015d4..5ff5e819b 100644
Binary files a/arknights_mower/resources/login_captcha.png and b/arknights_mower/resources/login_captcha.png differ
diff --git a/arknights_mower/resources/login_loading_1.png b/arknights_mower/resources/login_loading_1.png
deleted file mode 100644
index 05f8307e1..000000000
Binary files a/arknights_mower/resources/login_loading_1.png and /dev/null differ
diff --git a/arknights_mower/resources/login_new.png b/arknights_mower/resources/login_new.png
new file mode 100644
index 000000000..d98c95555
Binary files /dev/null and b/arknights_mower/resources/login_new.png differ
diff --git a/arknights_mower/resources/login_verify.png b/arknights_mower/resources/login_verify.png
deleted file mode 100644
index 85bd219fc..000000000
Binary files a/arknights_mower/resources/login_verify.png and /dev/null differ
diff --git a/arknights_mower/resources/materiel.png b/arknights_mower/resources/materiel.png
deleted file mode 100644
index 0b022d660..000000000
Binary files a/arknights_mower/resources/materiel.png and /dev/null differ
diff --git a/arknights_mower/resources/mission_trainee_on.png b/arknights_mower/resources/mission_trainee_on.png
index ec2e108e4..9721871a1 100644
Binary files a/arknights_mower/resources/mission_trainee_on.png and b/arknights_mower/resources/mission_trainee_on.png differ
diff --git a/arknights_mower/resources/nav_bar.png b/arknights_mower/resources/nav_bar.png
new file mode 100644
index 000000000..8089e2227
Binary files /dev/null and b/arknights_mower/resources/nav_bar.png differ
diff --git a/arknights_mower/resources/nav_index.png b/arknights_mower/resources/nav_index.png
deleted file mode 100644
index 8e34f3640..000000000
Binary files a/arknights_mower/resources/nav_index.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_infrastructure.png b/arknights_mower/resources/nav_infrastructure.png
deleted file mode 100644
index f169a22ba..000000000
Binary files a/arknights_mower/resources/nav_infrastructure.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_mission.png b/arknights_mower/resources/nav_mission.png
deleted file mode 100644
index 93abfba16..000000000
Binary files a/arknights_mower/resources/nav_mission.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_recruit.png b/arknights_mower/resources/nav_recruit.png
deleted file mode 100644
index 11833d7ca..000000000
Binary files a/arknights_mower/resources/nav_recruit.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_shop.png b/arknights_mower/resources/nav_shop.png
deleted file mode 100644
index f45a1730d..000000000
Binary files a/arknights_mower/resources/nav_shop.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_social.png b/arknights_mower/resources/nav_social.png
deleted file mode 100644
index 42b848847..000000000
Binary files a/arknights_mower/resources/nav_social.png and /dev/null differ
diff --git a/arknights_mower/resources/nav_terminal.png b/arknights_mower/resources/nav_terminal.png
deleted file mode 100644
index cbb8a8ac7..000000000
Binary files a/arknights_mower/resources/nav_terminal.png and /dev/null differ
diff --git a/arknights_mower/resources/navigation/act/0.png b/arknights_mower/resources/navigation/act/0.png
new file mode 100644
index 000000000..11a276e6b
Binary files /dev/null and b/arknights_mower/resources/navigation/act/0.png differ
diff --git a/arknights_mower/resources/navigation/act/1.png b/arknights_mower/resources/navigation/act/1.png
new file mode 100644
index 000000000..6274a652c
Binary files /dev/null and b/arknights_mower/resources/navigation/act/1.png differ
diff --git a/arknights_mower/resources/navigation/act/2.png b/arknights_mower/resources/navigation/act/2.png
new file mode 100644
index 000000000..4c10168c0
Binary files /dev/null and b/arknights_mower/resources/navigation/act/2.png differ
diff --git a/arknights_mower/resources/navigation/biography/OF_banner.png b/arknights_mower/resources/navigation/biography/OF_banner.png
new file mode 100644
index 000000000..ca6c60105
Binary files /dev/null and b/arknights_mower/resources/navigation/biography/OF_banner.png differ
diff --git a/arknights_mower/resources/navigation/biography/OF_entry.png b/arknights_mower/resources/navigation/biography/OF_entry.png
new file mode 100644
index 000000000..caaedb5d3
Binary files /dev/null and b/arknights_mower/resources/navigation/biography/OF_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/AP-1.png b/arknights_mower/resources/navigation/collection/AP-1.png
new file mode 100644
index 000000000..b51bd7215
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/AP-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/AP_entry.png b/arknights_mower/resources/navigation/collection/AP_entry.png
new file mode 100644
index 000000000..5fd60ec8d
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/AP_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/AP_not_available.png b/arknights_mower/resources/navigation/collection/AP_not_available.png
new file mode 100644
index 000000000..d23074356
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/AP_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/CA-1.png b/arknights_mower/resources/navigation/collection/CA-1.png
new file mode 100644
index 000000000..c22c58f6a
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CA-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/CA_entry.png b/arknights_mower/resources/navigation/collection/CA_entry.png
new file mode 100644
index 000000000..4935bb5ec
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CA_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/CA_not_available.png b/arknights_mower/resources/navigation/collection/CA_not_available.png
new file mode 100644
index 000000000..e1d2357ad
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CA_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/CE-1.png b/arknights_mower/resources/navigation/collection/CE-1.png
new file mode 100644
index 000000000..9181eb38e
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CE-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/CE_entry.png b/arknights_mower/resources/navigation/collection/CE_entry.png
new file mode 100644
index 000000000..2f80a2bd3
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CE_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/CE_not_available.png b/arknights_mower/resources/navigation/collection/CE_not_available.png
new file mode 100644
index 000000000..5e5514362
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/CE_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/LS-1.png b/arknights_mower/resources/navigation/collection/LS-1.png
new file mode 100644
index 000000000..0e494e8c9
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/LS-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/LS_entry.png b/arknights_mower/resources/navigation/collection/LS_entry.png
new file mode 100644
index 000000000..d8e838cb8
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/LS_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-A-1.png b/arknights_mower/resources/navigation/collection/PR-A-1.png
new file mode 100644
index 000000000..41c7589e9
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-A-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-A_entry.png b/arknights_mower/resources/navigation/collection/PR-A_entry.png
new file mode 100644
index 000000000..2e75476a2
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-A_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-A_not_available.png b/arknights_mower/resources/navigation/collection/PR-A_not_available.png
new file mode 100644
index 000000000..68c5cc5ae
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-A_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-B-1.png b/arknights_mower/resources/navigation/collection/PR-B-1.png
new file mode 100644
index 000000000..b19a97053
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-B-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-B_entry.png b/arknights_mower/resources/navigation/collection/PR-B_entry.png
new file mode 100644
index 000000000..e33766871
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-B_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-B_not_available.png b/arknights_mower/resources/navigation/collection/PR-B_not_available.png
new file mode 100644
index 000000000..40b688ef9
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-B_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-C-1.png b/arknights_mower/resources/navigation/collection/PR-C-1.png
new file mode 100644
index 000000000..935770a4f
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-C-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-C_entry.png b/arknights_mower/resources/navigation/collection/PR-C_entry.png
new file mode 100644
index 000000000..597edb3ce
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-C_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-C_not_available.png b/arknights_mower/resources/navigation/collection/PR-C_not_available.png
new file mode 100644
index 000000000..e18a53c51
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-C_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-D-1.png b/arknights_mower/resources/navigation/collection/PR-D-1.png
new file mode 100644
index 000000000..c9ea650dd
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-D-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-D_entry.png b/arknights_mower/resources/navigation/collection/PR-D_entry.png
new file mode 100644
index 000000000..bbc13b65a
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-D_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/PR-D_not_available.png b/arknights_mower/resources/navigation/collection/PR-D_not_available.png
new file mode 100644
index 000000000..5c39ea33c
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/PR-D_not_available.png differ
diff --git a/arknights_mower/resources/navigation/collection/SK-1.png b/arknights_mower/resources/navigation/collection/SK-1.png
new file mode 100644
index 000000000..329573408
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/SK-1.png differ
diff --git a/arknights_mower/resources/navigation/collection/SK_entry.png b/arknights_mower/resources/navigation/collection/SK_entry.png
new file mode 100644
index 000000000..7afc1fffc
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/SK_entry.png differ
diff --git a/arknights_mower/resources/navigation/collection/SK_not_available.png b/arknights_mower/resources/navigation/collection/SK_not_available.png
new file mode 100644
index 000000000..456c304c4
Binary files /dev/null and b/arknights_mower/resources/navigation/collection/SK_not_available.png differ
diff --git a/arknights_mower/resources/navigation/entry.png b/arknights_mower/resources/navigation/entry.png
new file mode 100644
index 000000000..93adfac88
Binary files /dev/null and b/arknights_mower/resources/navigation/entry.png differ
diff --git a/arknights_mower/resources/navigation/episode.png b/arknights_mower/resources/navigation/episode.png
new file mode 100644
index 000000000..e2f6e73bb
Binary files /dev/null and b/arknights_mower/resources/navigation/episode.png differ
diff --git a/arknights_mower/resources/main_0.png b/arknights_mower/resources/navigation/main/0.png
similarity index 100%
rename from arknights_mower/resources/main_0.png
rename to arknights_mower/resources/navigation/main/0.png
diff --git a/arknights_mower/resources/main_1.png b/arknights_mower/resources/navigation/main/1.png
similarity index 100%
rename from arknights_mower/resources/main_1.png
rename to arknights_mower/resources/navigation/main/1.png
diff --git a/arknights_mower/resources/main_10.png b/arknights_mower/resources/navigation/main/10.png
similarity index 100%
rename from arknights_mower/resources/main_10.png
rename to arknights_mower/resources/navigation/main/10.png
diff --git a/arknights_mower/resources/navigation/main/11.png b/arknights_mower/resources/navigation/main/11.png
new file mode 100644
index 000000000..01d3c072d
Binary files /dev/null and b/arknights_mower/resources/navigation/main/11.png differ
diff --git a/arknights_mower/resources/navigation/main/12.png b/arknights_mower/resources/navigation/main/12.png
new file mode 100644
index 000000000..d77691a0a
Binary files /dev/null and b/arknights_mower/resources/navigation/main/12.png differ
diff --git a/arknights_mower/resources/navigation/main/13.png b/arknights_mower/resources/navigation/main/13.png
new file mode 100644
index 000000000..b5ba56b2c
Binary files /dev/null and b/arknights_mower/resources/navigation/main/13.png differ
diff --git a/arknights_mower/resources/navigation/main/14.png b/arknights_mower/resources/navigation/main/14.png
new file mode 100644
index 000000000..1d44353e4
Binary files /dev/null and b/arknights_mower/resources/navigation/main/14.png differ
diff --git a/arknights_mower/resources/main_2.png b/arknights_mower/resources/navigation/main/2.png
similarity index 100%
rename from arknights_mower/resources/main_2.png
rename to arknights_mower/resources/navigation/main/2.png
diff --git a/arknights_mower/resources/main_3.png b/arknights_mower/resources/navigation/main/3.png
similarity index 100%
rename from arknights_mower/resources/main_3.png
rename to arknights_mower/resources/navigation/main/3.png
diff --git a/arknights_mower/resources/main_4.png b/arknights_mower/resources/navigation/main/4.png
similarity index 100%
rename from arknights_mower/resources/main_4.png
rename to arknights_mower/resources/navigation/main/4.png
diff --git a/arknights_mower/resources/main_5.png b/arknights_mower/resources/navigation/main/5.png
similarity index 100%
rename from arknights_mower/resources/main_5.png
rename to arknights_mower/resources/navigation/main/5.png
diff --git a/arknights_mower/resources/main_6.png b/arknights_mower/resources/navigation/main/6.png
similarity index 100%
rename from arknights_mower/resources/main_6.png
rename to arknights_mower/resources/navigation/main/6.png
diff --git a/arknights_mower/resources/main_7.png b/arknights_mower/resources/navigation/main/7.png
similarity index 100%
rename from arknights_mower/resources/main_7.png
rename to arknights_mower/resources/navigation/main/7.png
diff --git a/arknights_mower/resources/main_8.png b/arknights_mower/resources/navigation/main/8.png
similarity index 100%
rename from arknights_mower/resources/main_8.png
rename to arknights_mower/resources/navigation/main/8.png
diff --git a/arknights_mower/resources/main_9.png b/arknights_mower/resources/navigation/main/9.png
similarity index 100%
rename from arknights_mower/resources/main_9.png
rename to arknights_mower/resources/navigation/main/9.png
diff --git a/arknights_mower/resources/navigation/ope_difficulty.png b/arknights_mower/resources/navigation/ope_difficulty.png
new file mode 100644
index 000000000..bbe9f06c9
Binary files /dev/null and b/arknights_mower/resources/navigation/ope_difficulty.png differ
diff --git a/arknights_mower/resources/navigation/ope_hard.png b/arknights_mower/resources/navigation/ope_hard.png
new file mode 100644
index 000000000..1ee8db8bb
Binary files /dev/null and b/arknights_mower/resources/navigation/ope_hard.png differ
diff --git a/arknights_mower/resources/navigation/ope_hard_small.png b/arknights_mower/resources/navigation/ope_hard_small.png
new file mode 100644
index 000000000..e207eded5
Binary files /dev/null and b/arknights_mower/resources/navigation/ope_hard_small.png differ
diff --git a/arknights_mower/resources/navigation/ope_normal.png b/arknights_mower/resources/navigation/ope_normal.png
new file mode 100644
index 000000000..34f749ab5
Binary files /dev/null and b/arknights_mower/resources/navigation/ope_normal.png differ
diff --git a/arknights_mower/resources/navigation/ope_normal_small.png b/arknights_mower/resources/navigation/ope_normal_small.png
new file mode 100644
index 000000000..397b57d15
Binary files /dev/null and b/arknights_mower/resources/navigation/ope_normal_small.png differ
diff --git a/arknights_mower/resources/navigation/record_restoration.png b/arknights_mower/resources/navigation/record_restoration.png
new file mode 100644
index 000000000..2909d229f
Binary files /dev/null and b/arknights_mower/resources/navigation/record_restoration.png differ
diff --git a/arknights_mower/resources/network_check.png b/arknights_mower/resources/network_check.png
deleted file mode 100644
index 752bf6e21..000000000
Binary files a/arknights_mower/resources/network_check.png and /dev/null differ
diff --git a/arknights_mower/resources/next_step.png b/arknights_mower/resources/next_step.png
new file mode 100644
index 000000000..139a3525f
Binary files /dev/null and b/arknights_mower/resources/next_step.png differ
diff --git a/arknights_mower/resources/not_in_dorm.png b/arknights_mower/resources/not_in_dorm.png
deleted file mode 100644
index 450c05ea3..000000000
Binary files a/arknights_mower/resources/not_in_dorm.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_agency_fail.png b/arknights_mower/resources/ope_agency_fail.png
new file mode 100644
index 000000000..1a6039d90
Binary files /dev/null and b/arknights_mower/resources/ope_agency_fail.png differ
diff --git a/arknights_mower/resources/ope_agency_going.png b/arknights_mower/resources/ope_agency_going.png
index 398b7d867..8f859be99 100644
Binary files a/arknights_mower/resources/ope_agency_going.png and b/arknights_mower/resources/ope_agency_going.png differ
diff --git a/arknights_mower/resources/ope_agency_lock.png b/arknights_mower/resources/ope_agency_lock.png
new file mode 100644
index 000000000..8aca51ed0
Binary files /dev/null and b/arknights_mower/resources/ope_agency_lock.png differ
diff --git a/arknights_mower/resources/ope_agency_on.png b/arknights_mower/resources/ope_agency_on.png
deleted file mode 100644
index e3f321a32..000000000
Binary files a/arknights_mower/resources/ope_agency_on.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_elimi_agenct_used.png b/arknights_mower/resources/ope_elimi_agenct_used.png
deleted file mode 100644
index 0d3d04a1a..000000000
Binary files a/arknights_mower/resources/ope_elimi_agenct_used.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_elimi_agency_confirm.png b/arknights_mower/resources/ope_elimi_agency_confirm.png
index 45f936940..117c61e42 100644
Binary files a/arknights_mower/resources/ope_elimi_agency_confirm.png and b/arknights_mower/resources/ope_elimi_agency_confirm.png differ
diff --git a/arknights_mower/resources/ope_eliminate.png b/arknights_mower/resources/ope_eliminate.png
index ad001a733..6f0403555 100644
Binary files a/arknights_mower/resources/ope_eliminate.png and b/arknights_mower/resources/ope_eliminate.png differ
diff --git a/arknights_mower/resources/ope_enemy.png b/arknights_mower/resources/ope_enemy.png
deleted file mode 100644
index 7b8936a08..000000000
Binary files a/arknights_mower/resources/ope_enemy.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_firstdrop.png b/arknights_mower/resources/ope_firstdrop.png
deleted file mode 100644
index cdc7967cd..000000000
Binary files a/arknights_mower/resources/ope_firstdrop.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_giveup.png b/arknights_mower/resources/ope_giveup.png
deleted file mode 100644
index 78780523d..000000000
Binary files a/arknights_mower/resources/ope_giveup.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_killed.png b/arknights_mower/resources/ope_killed.png
deleted file mode 100644
index fdeb33715..000000000
Binary files a/arknights_mower/resources/ope_killed.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_ongoing.png b/arknights_mower/resources/ope_ongoing.png
deleted file mode 100644
index b5dcc8c49..000000000
Binary files a/arknights_mower/resources/ope_ongoing.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_choose.png b/arknights_mower/resources/ope_recover_choose.png
deleted file mode 100644
index 7295af178..000000000
Binary files a/arknights_mower/resources/ope_recover_choose.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_originite_choose.png b/arknights_mower/resources/ope_recover_originite_choose.png
deleted file mode 100644
index 31ad9e095..000000000
Binary files a/arknights_mower/resources/ope_recover_originite_choose.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_originite_empty.png b/arknights_mower/resources/ope_recover_originite_empty.png
deleted file mode 100644
index 1b28c9d1c..000000000
Binary files a/arknights_mower/resources/ope_recover_originite_empty.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_originite_no.png b/arknights_mower/resources/ope_recover_originite_no.png
deleted file mode 100644
index 9104a1723..000000000
Binary files a/arknights_mower/resources/ope_recover_originite_no.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_originite_yes.png b/arknights_mower/resources/ope_recover_originite_yes.png
deleted file mode 100644
index 758763cd7..000000000
Binary files a/arknights_mower/resources/ope_recover_originite_yes.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_potion_choose.png b/arknights_mower/resources/ope_recover_potion_choose.png
deleted file mode 100644
index 27d10883b..000000000
Binary files a/arknights_mower/resources/ope_recover_potion_choose.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_potion_empty.png b/arknights_mower/resources/ope_recover_potion_empty.png
deleted file mode 100644
index d7e3a49af..000000000
Binary files a/arknights_mower/resources/ope_recover_potion_empty.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_potion_no.png b/arknights_mower/resources/ope_recover_potion_no.png
deleted file mode 100644
index 4b10034fe..000000000
Binary files a/arknights_mower/resources/ope_recover_potion_no.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_recover_potion_yes.png b/arknights_mower/resources/ope_recover_potion_yes.png
deleted file mode 100644
index cdce5863b..000000000
Binary files a/arknights_mower/resources/ope_recover_potion_yes.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_sanity.png b/arknights_mower/resources/ope_sanity.png
deleted file mode 100644
index 5cd772f87..000000000
Binary files a/arknights_mower/resources/ope_sanity.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_select_start_empty.png b/arknights_mower/resources/ope_select_start_empty.png
new file mode 100644
index 000000000..0375aaeb3
Binary files /dev/null and b/arknights_mower/resources/ope_select_start_empty.png differ
diff --git a/arknights_mower/resources/ope_start_SN.png b/arknights_mower/resources/ope_start_SN.png
deleted file mode 100644
index e0c15ce3d..000000000
Binary files a/arknights_mower/resources/ope_start_SN.png and /dev/null differ
diff --git a/arknights_mower/resources/ope_top.png b/arknights_mower/resources/ope_top.png
deleted file mode 100644
index 7c9580944..000000000
Binary files a/arknights_mower/resources/ope_top.png and /dev/null differ
diff --git a/arknights_mower/resources/open_recruitment_tmp.png b/arknights_mower/resources/open_recruitment_tmp.png
deleted file mode 100644
index b45a3965f..000000000
Binary files a/arknights_mower/resources/open_recruitment_tmp.png and /dev/null differ
diff --git a/arknights_mower/resources/order_label.png b/arknights_mower/resources/order_label.png
new file mode 100644
index 000000000..7a8be9f2e
Binary files /dev/null and b/arknights_mower/resources/order_label.png differ
diff --git a/arknights_mower/resources/order_ready.png b/arknights_mower/resources/order_ready.png
new file mode 100644
index 000000000..ddb27f69b
Binary files /dev/null and b/arknights_mower/resources/order_ready.png differ
diff --git a/arknights_mower/resources/orders_time/0.png b/arknights_mower/resources/orders_time/0.png
new file mode 100644
index 000000000..a704de0ea
Binary files /dev/null and b/arknights_mower/resources/orders_time/0.png differ
diff --git a/arknights_mower/resources/orders_time/1.png b/arknights_mower/resources/orders_time/1.png
new file mode 100644
index 000000000..c499f6478
Binary files /dev/null and b/arknights_mower/resources/orders_time/1.png differ
diff --git a/arknights_mower/resources/orders_time/2.png b/arknights_mower/resources/orders_time/2.png
new file mode 100644
index 000000000..b3adb3373
Binary files /dev/null and b/arknights_mower/resources/orders_time/2.png differ
diff --git a/arknights_mower/resources/orders_time/3.png b/arknights_mower/resources/orders_time/3.png
new file mode 100644
index 000000000..5df5a5319
Binary files /dev/null and b/arknights_mower/resources/orders_time/3.png differ
diff --git a/arknights_mower/resources/orders_time/4.png b/arknights_mower/resources/orders_time/4.png
new file mode 100644
index 000000000..464cd4829
Binary files /dev/null and b/arknights_mower/resources/orders_time/4.png differ
diff --git a/arknights_mower/resources/orders_time/5.png b/arknights_mower/resources/orders_time/5.png
new file mode 100644
index 000000000..9dd107b1a
Binary files /dev/null and b/arknights_mower/resources/orders_time/5.png differ
diff --git a/arknights_mower/resources/orders_time/6.png b/arknights_mower/resources/orders_time/6.png
new file mode 100644
index 000000000..1c0ed2d31
Binary files /dev/null and b/arknights_mower/resources/orders_time/6.png differ
diff --git a/arknights_mower/resources/orders_time/7.png b/arknights_mower/resources/orders_time/7.png
new file mode 100644
index 000000000..bfad9bfe4
Binary files /dev/null and b/arknights_mower/resources/orders_time/7.png differ
diff --git a/arknights_mower/resources/orders_time/8.png b/arknights_mower/resources/orders_time/8.png
new file mode 100644
index 000000000..bd2fe098b
Binary files /dev/null and b/arknights_mower/resources/orders_time/8.png differ
diff --git a/arknights_mower/resources/orders_time/9.png b/arknights_mower/resources/orders_time/9.png
new file mode 100644
index 000000000..98a85f65f
Binary files /dev/null and b/arknights_mower/resources/orders_time/9.png differ
diff --git a/arknights_mower/resources/pull_once.png b/arknights_mower/resources/pull_once.png
new file mode 100644
index 000000000..0d888c958
Binary files /dev/null and b/arknights_mower/resources/pull_once.png differ
diff --git a/arknights_mower/resources/ra/action_points.png b/arknights_mower/resources/ra/action_points.png
new file mode 100644
index 000000000..e22aa1246
Binary files /dev/null and b/arknights_mower/resources/ra/action_points.png differ
diff --git a/arknights_mower/resources/ra/adventure.png b/arknights_mower/resources/ra/adventure.png
new file mode 100644
index 000000000..938b27b9c
Binary files /dev/null and b/arknights_mower/resources/ra/adventure.png differ
diff --git a/arknights_mower/resources/ra/adventure_ok.png b/arknights_mower/resources/ra/adventure_ok.png
new file mode 100644
index 000000000..8a66eeb86
Binary files /dev/null and b/arknights_mower/resources/ra/adventure_ok.png differ
diff --git a/arknights_mower/resources/ra/ap-1.png b/arknights_mower/resources/ra/ap-1.png
new file mode 100644
index 000000000..8f802c52f
Binary files /dev/null and b/arknights_mower/resources/ra/ap-1.png differ
diff --git a/arknights_mower/resources/ra/auto+1.png b/arknights_mower/resources/ra/auto+1.png
new file mode 100644
index 000000000..a98d903f9
Binary files /dev/null and b/arknights_mower/resources/ra/auto+1.png differ
diff --git a/arknights_mower/resources/ra/battle_complete.png b/arknights_mower/resources/ra/battle_complete.png
new file mode 100644
index 000000000..771425838
Binary files /dev/null and b/arknights_mower/resources/ra/battle_complete.png differ
diff --git a/arknights_mower/resources/ra/battle_exit.png b/arknights_mower/resources/ra/battle_exit.png
new file mode 100644
index 000000000..e37a1d9ab
Binary files /dev/null and b/arknights_mower/resources/ra/battle_exit.png differ
diff --git a/arknights_mower/resources/ra/battle_exit_dialog.png b/arknights_mower/resources/ra/battle_exit_dialog.png
new file mode 100644
index 000000000..de28caa54
Binary files /dev/null and b/arknights_mower/resources/ra/battle_exit_dialog.png differ
diff --git a/arknights_mower/resources/ra/click_anywhere.png b/arknights_mower/resources/ra/click_anywhere.png
new file mode 100644
index 000000000..4439e10ae
Binary files /dev/null and b/arknights_mower/resources/ra/click_anywhere.png differ
diff --git a/arknights_mower/resources/ra/click_to_continue.png b/arknights_mower/resources/ra/click_to_continue.png
new file mode 100644
index 000000000..8566517ec
Binary files /dev/null and b/arknights_mower/resources/ra/click_to_continue.png differ
diff --git a/arknights_mower/resources/ra/confirm_green.png b/arknights_mower/resources/ra/confirm_green.png
new file mode 100644
index 000000000..a4b0e24d6
Binary files /dev/null and b/arknights_mower/resources/ra/confirm_green.png differ
diff --git a/arknights_mower/resources/ra/confirm_red.png b/arknights_mower/resources/ra/confirm_red.png
new file mode 100644
index 000000000..41da74258
Binary files /dev/null and b/arknights_mower/resources/ra/confirm_red.png differ
diff --git a/arknights_mower/resources/ra/continue_button.png b/arknights_mower/resources/ra/continue_button.png
new file mode 100644
index 000000000..bc20087d3
Binary files /dev/null and b/arknights_mower/resources/ra/continue_button.png differ
diff --git a/arknights_mower/resources/ra/cook_button.png b/arknights_mower/resources/ra/cook_button.png
new file mode 100644
index 000000000..4809814be
Binary files /dev/null and b/arknights_mower/resources/ra/cook_button.png differ
diff --git a/arknights_mower/resources/ra/day_1.png b/arknights_mower/resources/ra/day_1.png
new file mode 100644
index 000000000..1b85f3d0f
Binary files /dev/null and b/arknights_mower/resources/ra/day_1.png differ
diff --git a/arknights_mower/resources/ra/day_2.png b/arknights_mower/resources/ra/day_2.png
new file mode 100644
index 000000000..1816df760
Binary files /dev/null and b/arknights_mower/resources/ra/day_2.png differ
diff --git a/arknights_mower/resources/ra/day_3.png b/arknights_mower/resources/ra/day_3.png
new file mode 100644
index 000000000..c941edc92
Binary files /dev/null and b/arknights_mower/resources/ra/day_3.png differ
diff --git a/arknights_mower/resources/ra/day_4.png b/arknights_mower/resources/ra/day_4.png
new file mode 100644
index 000000000..56174c410
Binary files /dev/null and b/arknights_mower/resources/ra/day_4.png differ
diff --git a/arknights_mower/resources/ra/day_complete.png b/arknights_mower/resources/ra/day_complete.png
new file mode 100644
index 000000000..a74af7ea7
Binary files /dev/null and b/arknights_mower/resources/ra/day_complete.png differ
diff --git a/arknights_mower/resources/ra/day_next.png b/arknights_mower/resources/ra/day_next.png
new file mode 100644
index 000000000..d83d56a10
Binary files /dev/null and b/arknights_mower/resources/ra/day_next.png differ
diff --git a/arknights_mower/resources/ra/days.png b/arknights_mower/resources/ra/days.png
new file mode 100644
index 000000000..280fb968e
Binary files /dev/null and b/arknights_mower/resources/ra/days.png differ
diff --git a/arknights_mower/resources/ra/delete_save.png b/arknights_mower/resources/ra/delete_save.png
new file mode 100644
index 000000000..eb83982e8
Binary files /dev/null and b/arknights_mower/resources/ra/delete_save.png differ
diff --git a/arknights_mower/resources/ra/delete_save_confirm_dialog.png b/arknights_mower/resources/ra/delete_save_confirm_dialog.png
new file mode 100644
index 000000000..f0958dd5f
Binary files /dev/null and b/arknights_mower/resources/ra/delete_save_confirm_dialog.png differ
diff --git a/arknights_mower/resources/ra/delete_save_confirm_dialog_ok_button.png b/arknights_mower/resources/ra/delete_save_confirm_dialog_ok_button.png
new file mode 100644
index 000000000..f767f56fe
Binary files /dev/null and b/arknights_mower/resources/ra/delete_save_confirm_dialog_ok_button.png differ
diff --git a/arknights_mower/resources/ra/dialog_cancel.png b/arknights_mower/resources/ra/dialog_cancel.png
new file mode 100644
index 000000000..39fc85ba8
Binary files /dev/null and b/arknights_mower/resources/ra/dialog_cancel.png differ
diff --git a/arknights_mower/resources/ra/drink_0.png b/arknights_mower/resources/ra/drink_0.png
new file mode 100644
index 000000000..64300b6e8
Binary files /dev/null and b/arknights_mower/resources/ra/drink_0.png differ
diff --git a/arknights_mower/resources/ra/drink_2.png b/arknights_mower/resources/ra/drink_2.png
new file mode 100644
index 000000000..e78f07175
Binary files /dev/null and b/arknights_mower/resources/ra/drink_2.png differ
diff --git a/arknights_mower/resources/ra/drink_4.png b/arknights_mower/resources/ra/drink_4.png
new file mode 100644
index 000000000..c4ccdd5af
Binary files /dev/null and b/arknights_mower/resources/ra/drink_4.png differ
diff --git a/arknights_mower/resources/ra/enter_battle_confirm_dialog.png b/arknights_mower/resources/ra/enter_battle_confirm_dialog.png
new file mode 100644
index 000000000..0c94ba2d1
Binary files /dev/null and b/arknights_mower/resources/ra/enter_battle_confirm_dialog.png differ
diff --git a/arknights_mower/resources/ra/get_item.png b/arknights_mower/resources/ra/get_item.png
new file mode 100644
index 000000000..08549d46b
Binary files /dev/null and b/arknights_mower/resources/ra/get_item.png differ
diff --git a/arknights_mower/resources/ra/guide_dialog.png b/arknights_mower/resources/ra/guide_dialog.png
new file mode 100644
index 000000000..4d6ebb6dc
Binary files /dev/null and b/arknights_mower/resources/ra/guide_dialog.png differ
diff --git a/arknights_mower/resources/ra/guide_entrance.png b/arknights_mower/resources/ra/guide_entrance.png
new file mode 100644
index 000000000..fbc596981
Binary files /dev/null and b/arknights_mower/resources/ra/guide_entrance.png differ
diff --git a/arknights_mower/resources/ra/main_title.png b/arknights_mower/resources/ra/main_title.png
new file mode 100644
index 000000000..ef30ccb38
Binary files /dev/null and b/arknights_mower/resources/ra/main_title.png differ
diff --git a/arknights_mower/resources/ra/map/base.png b/arknights_mower/resources/ra/map/base.png
new file mode 100644
index 000000000..e407de1c0
Binary files /dev/null and b/arknights_mower/resources/ra/map/base.png differ
diff --git "a/arknights_mower/resources/ra/map/\345\206\262\347\252\201\345\214\272_\344\270\242\345\244\261\347\232\204\350\256\242\345\215\225.png" "b/arknights_mower/resources/ra/map/\345\206\262\347\252\201\345\214\272_\344\270\242\345\244\261\347\232\204\350\256\242\345\215\225.png"
new file mode 100644
index 000000000..7d50ddd26
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\345\206\262\347\252\201\345\214\272_\344\270\242\345\244\261\347\232\204\350\256\242\345\215\225.png" differ
diff --git "a/arknights_mower/resources/ra/map/\345\220\216\350\210\215_\344\274\227\344\272\272\344\274\232\350\201\232\344\271\213\345\234\260.png" "b/arknights_mower/resources/ra/map/\345\220\216\350\210\215_\344\274\227\344\272\272\344\274\232\350\201\232\344\271\213\345\234\260.png"
new file mode 100644
index 000000000..72d0da324
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\345\220\216\350\210\215_\344\274\227\344\272\272\344\274\232\350\201\232\344\271\213\345\234\260.png" differ
diff --git "a/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\345\264\216\345\262\226\347\252\204\350\267\257.png" "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\345\264\216\345\262\226\347\252\204\350\267\257.png"
new file mode 100644
index 000000000..e35ede71d
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\345\264\216\345\262\226\347\252\204\350\267\257.png" differ
diff --git "a/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\347\240\276\346\262\231\345\271\263\345\216\237.png" "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\347\240\276\346\262\231\345\271\263\345\216\237.png"
new file mode 100644
index 000000000..bd46c7a3b
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\347\240\276\346\262\231\345\271\263\345\216\237.png" differ
diff --git "a/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\351\243\216\345\225\270\345\263\241\350\260\267.png" "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\351\243\216\345\225\270\345\263\241\350\260\267.png"
new file mode 100644
index 000000000..bb1db818f
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\345\245\207\351\201\207_\351\243\216\345\225\270\345\263\241\350\260\267.png" differ
diff --git "a/arknights_mower/resources/ra/map/\346\215\225\347\214\216\345\214\272_\350\201\232\347\276\275\344\271\213\345\234\260.png" "b/arknights_mower/resources/ra/map/\346\215\225\347\214\216\345\214\272_\350\201\232\347\276\275\344\271\213\345\234\260.png"
new file mode 100644
index 000000000..74c1f82ed
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\346\215\225\347\214\216\345\214\272_\350\201\232\347\276\275\344\271\213\345\234\260.png" differ
diff --git "a/arknights_mower/resources/ra/map/\350\246\201\345\241\236_\345\276\201\347\250\216\347\232\204\351\200\211\346\213\251.png" "b/arknights_mower/resources/ra/map/\350\246\201\345\241\236_\345\276\201\347\250\216\347\232\204\351\200\211\346\213\251.png"
new file mode 100644
index 000000000..325e51f64
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\350\246\201\345\241\236_\345\276\201\347\250\216\347\232\204\351\200\211\346\213\251.png" differ
diff --git "a/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\345\260\204\347\250\213\344\273\245\345\206\205.png" "b/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\345\260\204\347\250\213\344\273\245\345\206\205.png"
new file mode 100644
index 000000000..3ffe6dc07
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\345\260\204\347\250\213\344\273\245\345\206\205.png" differ
diff --git "a/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\346\236\227\344\270\255\345\257\273\345\256\235.png" "b/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\346\236\227\344\270\255\345\257\273\345\256\235.png"
new file mode 100644
index 000000000..c7ed2cbe3
Binary files /dev/null and "b/arknights_mower/resources/ra/map/\350\265\204\346\272\220\345\214\272_\346\236\227\344\270\255\345\257\273\345\256\235.png" differ
diff --git a/arknights_mower/resources/ra/map_back.png b/arknights_mower/resources/ra/map_back.png
new file mode 100644
index 000000000..2880bd7df
Binary files /dev/null and b/arknights_mower/resources/ra/map_back.png differ
diff --git a/arknights_mower/resources/ra/max.png b/arknights_mower/resources/ra/max.png
new file mode 100644
index 000000000..32c9e9f5b
Binary files /dev/null and b/arknights_mower/resources/ra/max.png differ
diff --git a/arknights_mower/resources/ra/no_enough_drink.png b/arknights_mower/resources/ra/no_enough_drink.png
new file mode 100644
index 000000000..df94b97f9
Binary files /dev/null and b/arknights_mower/resources/ra/no_enough_drink.png differ
diff --git a/arknights_mower/resources/ra/no_enough_resources.png b/arknights_mower/resources/ra/no_enough_resources.png
new file mode 100644
index 000000000..fc3b0cc41
Binary files /dev/null and b/arknights_mower/resources/ra/no_enough_resources.png differ
diff --git a/arknights_mower/resources/ra/notice.png b/arknights_mower/resources/ra/notice.png
new file mode 100644
index 000000000..d2ebc8102
Binary files /dev/null and b/arknights_mower/resources/ra/notice.png differ
diff --git a/arknights_mower/resources/ra/out_of_drink.png b/arknights_mower/resources/ra/out_of_drink.png
new file mode 100644
index 000000000..ad2ffa816
Binary files /dev/null and b/arknights_mower/resources/ra/out_of_drink.png differ
diff --git a/arknights_mower/resources/ra/period_complete.png b/arknights_mower/resources/ra/period_complete.png
new file mode 100644
index 000000000..eea79e40d
Binary files /dev/null and b/arknights_mower/resources/ra/period_complete.png differ
diff --git a/arknights_mower/resources/ra/period_complete_start_new_day.png b/arknights_mower/resources/ra/period_complete_start_new_day.png
new file mode 100644
index 000000000..5e3a34631
Binary files /dev/null and b/arknights_mower/resources/ra/period_complete_start_new_day.png differ
diff --git a/arknights_mower/resources/ra/popup.png b/arknights_mower/resources/ra/popup.png
new file mode 100644
index 000000000..a6bcadddc
Binary files /dev/null and b/arknights_mower/resources/ra/popup.png differ
diff --git a/arknights_mower/resources/ra/prepared_0.png b/arknights_mower/resources/ra/prepared_0.png
new file mode 100644
index 000000000..4e3ce75fe
Binary files /dev/null and b/arknights_mower/resources/ra/prepared_0.png differ
diff --git a/arknights_mower/resources/ra/prepared_1.png b/arknights_mower/resources/ra/prepared_1.png
new file mode 100644
index 000000000..289fe5a29
Binary files /dev/null and b/arknights_mower/resources/ra/prepared_1.png differ
diff --git a/arknights_mower/resources/ra/prepared_2.png b/arknights_mower/resources/ra/prepared_2.png
new file mode 100644
index 000000000..df4274a18
Binary files /dev/null and b/arknights_mower/resources/ra/prepared_2.png differ
diff --git a/arknights_mower/resources/ra/return_from_kitchen.png b/arknights_mower/resources/ra/return_from_kitchen.png
new file mode 100644
index 000000000..5c5b1063a
Binary files /dev/null and b/arknights_mower/resources/ra/return_from_kitchen.png differ
diff --git a/arknights_mower/resources/ra/save.png b/arknights_mower/resources/ra/save.png
new file mode 100644
index 000000000..b701e855d
Binary files /dev/null and b/arknights_mower/resources/ra/save.png differ
diff --git a/arknights_mower/resources/ra/shop.png b/arknights_mower/resources/ra/shop.png
new file mode 100644
index 000000000..8ab57b081
Binary files /dev/null and b/arknights_mower/resources/ra/shop.png differ
diff --git a/arknights_mower/resources/ra/spring.png b/arknights_mower/resources/ra/spring.png
new file mode 100644
index 000000000..dba792f47
Binary files /dev/null and b/arknights_mower/resources/ra/spring.png differ
diff --git a/arknights_mower/resources/ra/squad_back.png b/arknights_mower/resources/ra/squad_back.png
new file mode 100644
index 000000000..6828765d3
Binary files /dev/null and b/arknights_mower/resources/ra/squad_back.png differ
diff --git a/arknights_mower/resources/ra/squad_edit.png b/arknights_mower/resources/ra/squad_edit.png
new file mode 100644
index 000000000..72f47b1eb
Binary files /dev/null and b/arknights_mower/resources/ra/squad_edit.png differ
diff --git a/arknights_mower/resources/ra/squad_edit_confirm_dialog.png b/arknights_mower/resources/ra/squad_edit_confirm_dialog.png
new file mode 100644
index 000000000..fd8ee5e75
Binary files /dev/null and b/arknights_mower/resources/ra/squad_edit_confirm_dialog.png differ
diff --git a/arknights_mower/resources/ra/squad_edit_start_button.png b/arknights_mower/resources/ra/squad_edit_start_button.png
new file mode 100644
index 000000000..5ce3b1c89
Binary files /dev/null and b/arknights_mower/resources/ra/squad_edit_start_button.png differ
diff --git a/arknights_mower/resources/ra/start_action.png b/arknights_mower/resources/ra/start_action.png
new file mode 100644
index 000000000..c417cc044
Binary files /dev/null and b/arknights_mower/resources/ra/start_action.png differ
diff --git a/arknights_mower/resources/ra/start_button.png b/arknights_mower/resources/ra/start_button.png
new file mode 100644
index 000000000..57c0a3e5f
Binary files /dev/null and b/arknights_mower/resources/ra/start_button.png differ
diff --git a/arknights_mower/resources/ra/waste_time_button.png b/arknights_mower/resources/ra/waste_time_button.png
new file mode 100644
index 000000000..de7751e9e
Binary files /dev/null and b/arknights_mower/resources/ra/waste_time_button.png differ
diff --git a/arknights_mower/resources/ra/waste_time_dialog.png b/arknights_mower/resources/ra/waste_time_dialog.png
new file mode 100644
index 000000000..e28db1365
Binary files /dev/null and b/arknights_mower/resources/ra/waste_time_dialog.png differ
diff --git a/arknights_mower/resources/read_and_agree.png b/arknights_mower/resources/read_and_agree.png
new file mode 100644
index 000000000..205497e0d
Binary files /dev/null and b/arknights_mower/resources/read_and_agree.png differ
diff --git a/arknights_mower/resources/read_mail.png b/arknights_mower/resources/read_mail.png
index 180fcc9f9..d11dd312c 100644
Binary files a/arknights_mower/resources/read_mail.png and b/arknights_mower/resources/read_mail.png differ
diff --git a/arknights_mower/resources/recruit.png b/arknights_mower/resources/recruit.png
deleted file mode 100644
index 308fffeea..000000000
Binary files a/arknights_mower/resources/recruit.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit/agent_token.png b/arknights_mower/resources/recruit/agent_token.png
new file mode 100644
index 000000000..3d4b649bd
Binary files /dev/null and b/arknights_mower/resources/recruit/agent_token.png differ
diff --git a/arknights_mower/resources/recruit/agent_token_first.png b/arknights_mower/resources/recruit/agent_token_first.png
new file mode 100644
index 000000000..13b298259
Binary files /dev/null and b/arknights_mower/resources/recruit/agent_token_first.png differ
diff --git a/arknights_mower/resources/available_level.png b/arknights_mower/resources/recruit/available_level.png
similarity index 100%
rename from arknights_mower/resources/available_level.png
rename to arknights_mower/resources/recruit/available_level.png
diff --git a/arknights_mower/resources/recruit/begin_recruit.png b/arknights_mower/resources/recruit/begin_recruit.png
new file mode 100644
index 000000000..0954a7382
Binary files /dev/null and b/arknights_mower/resources/recruit/begin_recruit.png differ
diff --git a/arknights_mower/resources/career_needs.png b/arknights_mower/resources/recruit/career_needs.png
similarity index 100%
rename from arknights_mower/resources/career_needs.png
rename to arknights_mower/resources/recruit/career_needs.png
diff --git a/arknights_mower/resources/job_requirements.png b/arknights_mower/resources/recruit/job_requirements.png
similarity index 100%
rename from arknights_mower/resources/job_requirements.png
rename to arknights_mower/resources/recruit/job_requirements.png
diff --git a/arknights_mower/resources/recruit/lmb.png b/arknights_mower/resources/recruit/lmb.png
new file mode 100644
index 000000000..63dbc5dda
Binary files /dev/null and b/arknights_mower/resources/recruit/lmb.png differ
diff --git a/arknights_mower/resources/recruit/recruit_done.png b/arknights_mower/resources/recruit/recruit_done.png
new file mode 100644
index 000000000..181d1fbe1
Binary files /dev/null and b/arknights_mower/resources/recruit/recruit_done.png differ
diff --git a/arknights_mower/resources/recruit/recruit_lock.png b/arknights_mower/resources/recruit/recruit_lock.png
new file mode 100644
index 000000000..c43bb58e9
Binary files /dev/null and b/arknights_mower/resources/recruit/recruit_lock.png differ
diff --git a/arknights_mower/resources/recruit/refresh.png b/arknights_mower/resources/recruit/refresh.png
new file mode 100644
index 000000000..8d3fb3e99
Binary files /dev/null and b/arknights_mower/resources/recruit/refresh.png differ
diff --git a/arknights_mower/resources/recruit/refresh_comfirm.png b/arknights_mower/resources/recruit/refresh_comfirm.png
new file mode 100644
index 000000000..5f6e05fc3
Binary files /dev/null and b/arknights_mower/resources/recruit/refresh_comfirm.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/CASTER.png b/arknights_mower/resources/recruit/riic_res/CASTER.png
new file mode 100644
index 000000000..fee4f5a4b
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/CASTER.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/MEDIC.png b/arknights_mower/resources/recruit/riic_res/MEDIC.png
new file mode 100644
index 000000000..19c4c47df
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/MEDIC.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/PIONEER.png b/arknights_mower/resources/recruit/riic_res/PIONEER.png
new file mode 100644
index 000000000..77defdf22
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/PIONEER.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/SNIPER.png b/arknights_mower/resources/recruit/riic_res/SNIPER.png
new file mode 100644
index 000000000..4ced1d41a
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/SNIPER.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/SPECIAL.png b/arknights_mower/resources/recruit/riic_res/SPECIAL.png
new file mode 100644
index 000000000..f0592b04e
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/SPECIAL.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/SUPPORT.png b/arknights_mower/resources/recruit/riic_res/SUPPORT.png
new file mode 100644
index 000000000..8aa9ee364
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/SUPPORT.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/TANK.png b/arknights_mower/resources/recruit/riic_res/TANK.png
new file mode 100644
index 000000000..d4873691f
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/TANK.png differ
diff --git a/arknights_mower/resources/recruit/riic_res/WARRIOR.png b/arknights_mower/resources/recruit/riic_res/WARRIOR.png
new file mode 100644
index 000000000..35ccda1b9
Binary files /dev/null and b/arknights_mower/resources/recruit/riic_res/WARRIOR.png differ
diff --git a/arknights_mower/resources/recruit/start_recruit.png b/arknights_mower/resources/recruit/start_recruit.png
new file mode 100644
index 000000000..d1baa1d74
Binary files /dev/null and b/arknights_mower/resources/recruit/start_recruit.png differ
diff --git a/arknights_mower/resources/recruit/stone.png b/arknights_mower/resources/recruit/stone.png
new file mode 100644
index 000000000..e04e20dcc
Binary files /dev/null and b/arknights_mower/resources/recruit/stone.png differ
diff --git a/arknights_mower/resources/recruit/ticket.png b/arknights_mower/resources/recruit/ticket.png
new file mode 100644
index 000000000..eb49acaff
Binary files /dev/null and b/arknights_mower/resources/recruit/ticket.png differ
diff --git a/arknights_mower/resources/recruit/time.png b/arknights_mower/resources/recruit/time.png
new file mode 100644
index 000000000..1fb776ef8
Binary files /dev/null and b/arknights_mower/resources/recruit/time.png differ
diff --git a/arknights_mower/resources/recruit_2.png b/arknights_mower/resources/recruit_2.png
deleted file mode 100644
index 1922cde05..000000000
Binary files a/arknights_mower/resources/recruit_2.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_4.png b/arknights_mower/resources/recruit_4.png
deleted file mode 100644
index 3a9294ec2..000000000
Binary files a/arknights_mower/resources/recruit_4.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_4_chose.png b/arknights_mower/resources/recruit_4_chose.png
deleted file mode 100644
index 37bde9cc6..000000000
Binary files a/arknights_mower/resources/recruit_4_chose.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_budget.png b/arknights_mower/resources/recruit_budget.png
deleted file mode 100644
index f60365113..000000000
Binary files a/arknights_mower/resources/recruit_budget.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_finish.png b/arknights_mower/resources/recruit_finish.png
deleted file mode 100644
index e4f6b197f..000000000
Binary files a/arknights_mower/resources/recruit_finish.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_no.png b/arknights_mower/resources/recruit_no.png
deleted file mode 100644
index 1fb277bdd..000000000
Binary files a/arknights_mower/resources/recruit_no.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_no_refresh.png b/arknights_mower/resources/recruit_no_refresh.png
deleted file mode 100644
index 9a270eb11..000000000
Binary files a/arknights_mower/resources/recruit_no_refresh.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_no_ticket.png b/arknights_mower/resources/recruit_no_ticket.png
deleted file mode 100644
index a4fedf97a..000000000
Binary files a/arknights_mower/resources/recruit_no_ticket.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_panel.png b/arknights_mower/resources/recruit_panel.png
deleted file mode 100644
index 29d5e2fcd..000000000
Binary files a/arknights_mower/resources/recruit_panel.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_refresh.png b/arknights_mower/resources/recruit_refresh.png
deleted file mode 100644
index 9fb5e47a0..000000000
Binary files a/arknights_mower/resources/recruit_refresh.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_special.png b/arknights_mower/resources/recruit_special.png
deleted file mode 100644
index 28938bf7f..000000000
Binary files a/arknights_mower/resources/recruit_special.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_tags.png b/arknights_mower/resources/recruit_tags.png
deleted file mode 100644
index 8b2b71342..000000000
Binary files a/arknights_mower/resources/recruit_tags.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_yes.png b/arknights_mower/resources/recruit_yes.png
deleted file mode 100644
index 7dddff00f..000000000
Binary files a/arknights_mower/resources/recruit_yes.png and /dev/null differ
diff --git a/arknights_mower/resources/recruit_yesorno.png b/arknights_mower/resources/recruit_yesorno.png
deleted file mode 100644
index 31321b24f..000000000
Binary files a/arknights_mower/resources/recruit_yesorno.png and /dev/null differ
diff --git a/arknights_mower/resources/register.png b/arknights_mower/resources/register.png
deleted file mode 100644
index 013ca6a3d..000000000
Binary files a/arknights_mower/resources/register.png and /dev/null differ
diff --git a/arknights_mower/resources/riic/assistants.png b/arknights_mower/resources/riic/assistants.png
new file mode 100644
index 000000000..c869a6edb
Binary files /dev/null and b/arknights_mower/resources/riic/assistants.png differ
diff --git a/arknights_mower/resources/riic/exp.png b/arknights_mower/resources/riic/exp.png
new file mode 100644
index 000000000..da3df4e1d
Binary files /dev/null and b/arknights_mower/resources/riic/exp.png differ
diff --git a/arknights_mower/resources/riic/iron.png b/arknights_mower/resources/riic/iron.png
new file mode 100644
index 000000000..4cc138180
Binary files /dev/null and b/arknights_mower/resources/riic/iron.png differ
diff --git a/arknights_mower/resources/riic/manufacture.png b/arknights_mower/resources/riic/manufacture.png
new file mode 100644
index 000000000..08f7dcb89
Binary files /dev/null and b/arknights_mower/resources/riic/manufacture.png differ
diff --git a/arknights_mower/resources/riic/orundum.png b/arknights_mower/resources/riic/orundum.png
new file mode 100644
index 000000000..0c41fe31f
Binary files /dev/null and b/arknights_mower/resources/riic/orundum.png differ
diff --git a/arknights_mower/resources/riic/report_title.png b/arknights_mower/resources/riic/report_title.png
new file mode 100644
index 000000000..a8c7ffed7
Binary files /dev/null and b/arknights_mower/resources/riic/report_title.png differ
diff --git a/arknights_mower/resources/riic/trade.png b/arknights_mower/resources/riic/trade.png
new file mode 100644
index 000000000..7b20a5895
Binary files /dev/null and b/arknights_mower/resources/riic/trade.png differ
diff --git a/arknights_mower/resources/room/1.png b/arknights_mower/resources/room/1.png
new file mode 100644
index 000000000..34f11a002
Binary files /dev/null and b/arknights_mower/resources/room/1.png differ
diff --git a/arknights_mower/resources/room/2.png b/arknights_mower/resources/room/2.png
new file mode 100644
index 000000000..25610e092
Binary files /dev/null and b/arknights_mower/resources/room/2.png differ
diff --git a/arknights_mower/resources/room/3.png b/arknights_mower/resources/room/3.png
new file mode 100644
index 000000000..c028d1787
Binary files /dev/null and b/arknights_mower/resources/room/3.png differ
diff --git a/arknights_mower/resources/room/4.png b/arknights_mower/resources/room/4.png
new file mode 100644
index 000000000..e1049ac07
Binary files /dev/null and b/arknights_mower/resources/room/4.png differ
diff --git a/arknights_mower/resources/room/central.png b/arknights_mower/resources/room/central.png
new file mode 100644
index 000000000..9b75ff53e
Binary files /dev/null and b/arknights_mower/resources/room/central.png differ
diff --git a/arknights_mower/resources/room/contact.png b/arknights_mower/resources/room/contact.png
new file mode 100644
index 000000000..3dab26aa4
Binary files /dev/null and b/arknights_mower/resources/room/contact.png differ
diff --git a/arknights_mower/resources/room/dormitory.png b/arknights_mower/resources/room/dormitory.png
new file mode 100644
index 000000000..424f38003
Binary files /dev/null and b/arknights_mower/resources/room/dormitory.png differ
diff --git a/arknights_mower/resources/room/meeting.png b/arknights_mower/resources/room/meeting.png
new file mode 100644
index 000000000..30124d00e
Binary files /dev/null and b/arknights_mower/resources/room/meeting.png differ
diff --git a/arknights_mower/resources/sanity.png b/arknights_mower/resources/sanity.png
new file mode 100644
index 000000000..c8e459a7b
Binary files /dev/null and b/arknights_mower/resources/sanity.png differ
diff --git a/arknights_mower/resources/sf/available.png b/arknights_mower/resources/sf/available.png
new file mode 100644
index 000000000..1d6cbb20f
Binary files /dev/null and b/arknights_mower/resources/sf/available.png differ
diff --git a/arknights_mower/resources/sf/card.png b/arknights_mower/resources/sf/card.png
new file mode 100644
index 000000000..3f30227ca
Binary files /dev/null and b/arknights_mower/resources/sf/card.png differ
diff --git a/arknights_mower/resources/sf/click_anywhere.png b/arknights_mower/resources/sf/click_anywhere.png
new file mode 100644
index 000000000..34250c4dc
Binary files /dev/null and b/arknights_mower/resources/sf/click_anywhere.png differ
diff --git a/arknights_mower/resources/sf/confirm.png b/arknights_mower/resources/sf/confirm.png
new file mode 100644
index 000000000..4cedd1ad1
Binary files /dev/null and b/arknights_mower/resources/sf/confirm.png differ
diff --git a/arknights_mower/resources/sf/continue.png b/arknights_mower/resources/sf/continue.png
new file mode 100644
index 000000000..914184ad6
Binary files /dev/null and b/arknights_mower/resources/sf/continue.png differ
diff --git a/arknights_mower/resources/sf/continue_event.png b/arknights_mower/resources/sf/continue_event.png
new file mode 100644
index 000000000..92e3b88cb
Binary files /dev/null and b/arknights_mower/resources/sf/continue_event.png differ
diff --git a/arknights_mower/resources/sf/continue_result.png b/arknights_mower/resources/sf/continue_result.png
new file mode 100644
index 000000000..886fce42c
Binary files /dev/null and b/arknights_mower/resources/sf/continue_result.png differ
diff --git a/arknights_mower/resources/sf/end.png b/arknights_mower/resources/sf/end.png
new file mode 100644
index 000000000..310645c7c
Binary files /dev/null and b/arknights_mower/resources/sf/end.png differ
diff --git a/arknights_mower/resources/sf/entrance.png b/arknights_mower/resources/sf/entrance.png
new file mode 100644
index 000000000..35366572d
Binary files /dev/null and b/arknights_mower/resources/sf/entrance.png differ
diff --git a/arknights_mower/resources/sf/exit.png b/arknights_mower/resources/sf/exit.png
new file mode 100644
index 000000000..acf7f769f
Binary files /dev/null and b/arknights_mower/resources/sf/exit.png differ
diff --git a/arknights_mower/resources/sf/exit_button.png b/arknights_mower/resources/sf/exit_button.png
new file mode 100644
index 000000000..5f0fb2be5
Binary files /dev/null and b/arknights_mower/resources/sf/exit_button.png differ
diff --git a/arknights_mower/resources/sf/failure.png b/arknights_mower/resources/sf/failure.png
new file mode 100644
index 000000000..c67f36756
Binary files /dev/null and b/arknights_mower/resources/sf/failure.png differ
diff --git a/arknights_mower/resources/sf/info.png b/arknights_mower/resources/sf/info.png
new file mode 100644
index 000000000..33e9b2526
Binary files /dev/null and b/arknights_mower/resources/sf/info.png differ
diff --git a/arknights_mower/resources/sf/inheritance.png b/arknights_mower/resources/sf/inheritance.png
new file mode 100644
index 000000000..3566c05c2
Binary files /dev/null and b/arknights_mower/resources/sf/inheritance.png differ
diff --git a/arknights_mower/resources/sf/lost_in_the_trick.png b/arknights_mower/resources/sf/lost_in_the_trick.png
new file mode 100644
index 000000000..032ab0ff3
Binary files /dev/null and b/arknights_mower/resources/sf/lost_in_the_trick.png differ
diff --git a/arknights_mower/resources/sf/percentage.png b/arknights_mower/resources/sf/percentage.png
new file mode 100644
index 000000000..106959bc6
Binary files /dev/null and b/arknights_mower/resources/sf/percentage.png differ
diff --git a/arknights_mower/resources/sf/properties.png b/arknights_mower/resources/sf/properties.png
new file mode 100644
index 000000000..c00d585c8
Binary files /dev/null and b/arknights_mower/resources/sf/properties.png differ
diff --git a/arknights_mower/resources/sf/ranger.png b/arknights_mower/resources/sf/ranger.png
new file mode 100644
index 000000000..316cd0f2f
Binary files /dev/null and b/arknights_mower/resources/sf/ranger.png differ
diff --git a/arknights_mower/resources/sf/restart.png b/arknights_mower/resources/sf/restart.png
new file mode 100644
index 000000000..5cdfc16d1
Binary files /dev/null and b/arknights_mower/resources/sf/restart.png differ
diff --git a/arknights_mower/resources/sf/select.png b/arknights_mower/resources/sf/select.png
new file mode 100644
index 000000000..49e2e644e
Binary files /dev/null and b/arknights_mower/resources/sf/select.png differ
diff --git a/arknights_mower/resources/sf/select_team_ok.png b/arknights_mower/resources/sf/select_team_ok.png
new file mode 100644
index 000000000..0877d8f8d
Binary files /dev/null and b/arknights_mower/resources/sf/select_team_ok.png differ
diff --git a/arknights_mower/resources/sf/success.png b/arknights_mower/resources/sf/success.png
new file mode 100644
index 000000000..dece7b23e
Binary files /dev/null and b/arknights_mower/resources/sf/success.png differ
diff --git a/arknights_mower/resources/sf/support_battle_platform.png b/arknights_mower/resources/sf/support_battle_platform.png
new file mode 100644
index 000000000..ca12ce638
Binary files /dev/null and b/arknights_mower/resources/sf/support_battle_platform.png differ
diff --git a/arknights_mower/resources/sf/team_intelligence.png b/arknights_mower/resources/sf/team_intelligence.png
new file mode 100644
index 000000000..d6c9a1da9
Binary files /dev/null and b/arknights_mower/resources/sf/team_intelligence.png differ
diff --git a/arknights_mower/resources/sf/team_management.png b/arknights_mower/resources/sf/team_management.png
new file mode 100644
index 000000000..9bc1f0fc1
Binary files /dev/null and b/arknights_mower/resources/sf/team_management.png differ
diff --git a/arknights_mower/resources/sf/team_medicine.png b/arknights_mower/resources/sf/team_medicine.png
new file mode 100644
index 000000000..5f6642f81
Binary files /dev/null and b/arknights_mower/resources/sf/team_medicine.png differ
diff --git a/arknights_mower/resources/sf/team_pass.png b/arknights_mower/resources/sf/team_pass.png
new file mode 100644
index 000000000..3cf227135
Binary files /dev/null and b/arknights_mower/resources/sf/team_pass.png differ
diff --git a/arknights_mower/resources/shop_credit_on.png b/arknights_mower/resources/shop_credit_on.png
deleted file mode 100644
index 13bfad195..000000000
Binary files a/arknights_mower/resources/shop_credit_on.png and /dev/null differ
diff --git a/arknights_mower/resources/shop_discount_sold/50.png b/arknights_mower/resources/shop_discount_sold/50.png
new file mode 100644
index 000000000..93192c33a
Binary files /dev/null and b/arknights_mower/resources/shop_discount_sold/50.png differ
diff --git a/arknights_mower/resources/shop_discount_sold/75.png b/arknights_mower/resources/shop_discount_sold/75.png
new file mode 100644
index 000000000..7aa15ba72
Binary files /dev/null and b/arknights_mower/resources/shop_discount_sold/75.png differ
diff --git a/arknights_mower/resources/shop_discount_sold/95.png b/arknights_mower/resources/shop_discount_sold/95.png
new file mode 100644
index 000000000..00897da15
Binary files /dev/null and b/arknights_mower/resources/shop_discount_sold/95.png differ
diff --git a/arknights_mower/resources/shop_discount_sold/99.png b/arknights_mower/resources/shop_discount_sold/99.png
new file mode 100644
index 000000000..886fe89c4
Binary files /dev/null and b/arknights_mower/resources/shop_discount_sold/99.png differ
diff --git a/arknights_mower/resources/shop_sold.png b/arknights_mower/resources/shop_sold.png
deleted file mode 100644
index 154acb835..000000000
Binary files a/arknights_mower/resources/shop_sold.png and /dev/null differ
diff --git a/arknights_mower/resources/skill_collect_confirm.png b/arknights_mower/resources/skill_collect_confirm.png
new file mode 100644
index 000000000..b1ab97125
Binary files /dev/null and b/arknights_mower/resources/skill_collect_confirm.png differ
diff --git a/arknights_mower/resources/skill_confirm.png b/arknights_mower/resources/skill_confirm.png
new file mode 100644
index 000000000..b9781c42a
Binary files /dev/null and b/arknights_mower/resources/skill_confirm.png differ
diff --git a/arknights_mower/resources/spent_credit.png b/arknights_mower/resources/spent_credit.png
new file mode 100644
index 000000000..6b5893ef3
Binary files /dev/null and b/arknights_mower/resources/spent_credit.png differ
diff --git a/arknights_mower/resources/sss/close_button.png b/arknights_mower/resources/sss/close_button.png
new file mode 100644
index 000000000..db893f345
Binary files /dev/null and b/arknights_mower/resources/sss/close_button.png differ
diff --git a/arknights_mower/resources/sss/deploy_button.png b/arknights_mower/resources/sss/deploy_button.png
new file mode 100644
index 000000000..7bf4c47ce
Binary files /dev/null and b/arknights_mower/resources/sss/deploy_button.png differ
diff --git a/arknights_mower/resources/sss/device_button.png b/arknights_mower/resources/sss/device_button.png
new file mode 100644
index 000000000..026e5a4e2
Binary files /dev/null and b/arknights_mower/resources/sss/device_button.png differ
diff --git a/arknights_mower/resources/sss/ec_button.png b/arknights_mower/resources/sss/ec_button.png
new file mode 100644
index 000000000..500a45c6b
Binary files /dev/null and b/arknights_mower/resources/sss/ec_button.png differ
diff --git a/arknights_mower/resources/sss/loading.png b/arknights_mower/resources/sss/loading.png
new file mode 100644
index 000000000..6c491e306
Binary files /dev/null and b/arknights_mower/resources/sss/loading.png differ
diff --git a/arknights_mower/resources/sss/main.png b/arknights_mower/resources/sss/main.png
new file mode 100644
index 000000000..78c8a2e08
Binary files /dev/null and b/arknights_mower/resources/sss/main.png differ
diff --git a/arknights_mower/resources/sss/redeploy_button.png b/arknights_mower/resources/sss/redeploy_button.png
new file mode 100644
index 000000000..117cf5241
Binary files /dev/null and b/arknights_mower/resources/sss/redeploy_button.png differ
diff --git a/arknights_mower/resources/sss/squad_button.png b/arknights_mower/resources/sss/squad_button.png
new file mode 100644
index 000000000..3f755ef5a
Binary files /dev/null and b/arknights_mower/resources/sss/squad_button.png differ
diff --git a/arknights_mower/resources/sss/start_button.png b/arknights_mower/resources/sss/start_button.png
new file mode 100644
index 000000000..3b20a209b
Binary files /dev/null and b/arknights_mower/resources/sss/start_button.png differ
diff --git a/arknights_mower/resources/start_text.png b/arknights_mower/resources/start_text.png
deleted file mode 100644
index f4076824f..000000000
Binary files a/arknights_mower/resources/start_text.png and /dev/null differ
diff --git a/arknights_mower/resources/stone.png b/arknights_mower/resources/stone.png
new file mode 100644
index 000000000..0edb83bfa
Binary files /dev/null and b/arknights_mower/resources/stone.png differ
diff --git a/arknights_mower/resources/terminal_eliminate.png b/arknights_mower/resources/terminal_eliminate.png
index 4e08daad9..07b000831 100644
Binary files a/arknights_mower/resources/terminal_eliminate.png and b/arknights_mower/resources/terminal_eliminate.png differ
diff --git a/arknights_mower/resources/terminal_longterm.png b/arknights_mower/resources/terminal_longterm.png
new file mode 100644
index 000000000..3f600df50
Binary files /dev/null and b/arknights_mower/resources/terminal_longterm.png differ
diff --git a/arknights_mower/resources/terminal_longterm_reclamation_algorithm.png b/arknights_mower/resources/terminal_longterm_reclamation_algorithm.png
new file mode 100644
index 000000000..8c24ba040
Binary files /dev/null and b/arknights_mower/resources/terminal_longterm_reclamation_algorithm.png differ
diff --git a/arknights_mower/resources/terminal_main.png b/arknights_mower/resources/terminal_main.png
new file mode 100644
index 000000000..4f0ea418f
Binary files /dev/null and b/arknights_mower/resources/terminal_main.png differ
diff --git a/arknights_mower/resources/terminal_pre.png b/arknights_mower/resources/terminal_pre.png
index bbbdcea08..da66ef9a1 100644
Binary files a/arknights_mower/resources/terminal_pre.png and b/arknights_mower/resources/terminal_pre.png differ
diff --git a/arknights_mower/resources/terminal_regular.png b/arknights_mower/resources/terminal_regular.png
new file mode 100644
index 000000000..d5c915fb7
Binary files /dev/null and b/arknights_mower/resources/terminal_regular.png differ
diff --git a/arknights_mower/resources/terminal_small.png b/arknights_mower/resources/terminal_small.png
deleted file mode 100644
index 17bd98ac9..000000000
Binary files a/arknights_mower/resources/terminal_small.png and /dev/null differ
diff --git a/arknights_mower/resources/train_main.png b/arknights_mower/resources/train_main.png
new file mode 100644
index 000000000..5f1fd88be
Binary files /dev/null and b/arknights_mower/resources/train_main.png differ
diff --git a/arknights_mower/resources/training_completed.png b/arknights_mower/resources/training_completed.png
new file mode 100644
index 000000000..5f5186fd4
Binary files /dev/null and b/arknights_mower/resources/training_completed.png differ
diff --git a/arknights_mower/resources/training_support.png b/arknights_mower/resources/training_support.png
new file mode 100644
index 000000000..e77480610
Binary files /dev/null and b/arknights_mower/resources/training_support.png differ
diff --git a/arknights_mower/resources/upgrade_failure.png b/arknights_mower/resources/upgrade_failure.png
new file mode 100644
index 000000000..2c1ffc6b9
Binary files /dev/null and b/arknights_mower/resources/upgrade_failure.png differ
diff --git a/arknights_mower/resources/yes_big.png b/arknights_mower/resources/yes_big.png
deleted file mode 100644
index 193cf4431..000000000
Binary files a/arknights_mower/resources/yes_big.png and /dev/null differ
diff --git a/arknights_mower/resources/yes_small.png b/arknights_mower/resources/yes_small.png
deleted file mode 100644
index 82d9eec1e..000000000
Binary files a/arknights_mower/resources/yes_small.png and /dev/null differ
diff --git a/arknights_mower/solvers/__init__.py b/arknights_mower/solvers/__init__.py
index 542b95a73..e69de29bb 100644
--- a/arknights_mower/solvers/__init__.py
+++ b/arknights_mower/solvers/__init__.py
@@ -1,8 +0,0 @@
-from .base_construct import BaseConstructSolver
-from .credit import CreditSolver
-from .mail import MailSolver
-from .mission import MissionSolver
-from .operation import OpeSolver
-from .recruit import RecruitSolver
-from .schedule import ScheduleSolver
-from .shop import ShopSolver
diff --git a/arknights_mower/solvers/auto_fight.py b/arknights_mower/solvers/auto_fight.py
new file mode 100644
index 000000000..bc85085a0
--- /dev/null
+++ b/arknights_mower/solvers/auto_fight.py
@@ -0,0 +1,320 @@
+from time import sleep
+
+import cv2
+from scipy.signal import argrelmax
+from skimage.metrics import structural_similarity
+
+from arknights_mower.models import avatar, secret_front
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.image import cropimg, loadres, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.solver import BaseSolver
+from arknights_mower.utils.tile_pos import Calc, find_level
+from arknights_mower.utils.vector import sa, va
+
+
+class AutoFight(BaseSolver):
+ def run(self, level_name, opers, actions):
+ logger.info("Start: 自动战斗")
+ logger.info("地图坐标计算:https://github.com/yuanyan3060/Arknights-Tile-Pos")
+ level = find_level(level_name, None)
+ self.calc = Calc(1920, 1080, level)
+ self.actions = actions
+ self.speed = 1 # 一倍/二倍速
+ self.loading = True # 等待加载
+ self.playing = True # 暂停/继续
+ self.operators = {} # 可部署的干员
+ self.location = {} # 干员部署坐标
+
+ self.watching = {} # 需要开技能的干员
+ for op in opers:
+ name = op["name"]
+ del op["name"]
+ self.watching[name] = op
+ self.watching[name]["location"] = None
+
+ super().run()
+
+ def number(self, scope: tp.Scope, height: int, thres: int) -> int:
+ "数字识别"
+ img = cropimg(self.recog.gray, scope)
+ default_height = 25
+ if height != default_height:
+ scale = 25 / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, thres)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+
+ value = 0
+
+ for x, y, w, h in rect:
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ score = []
+ for i in range(10):
+ im = secret_front[i]
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+
+ return value
+
+ def cost(self) -> int:
+ "获取部署费用"
+ cost = self.number(((1800, 745), (1920, 805)), 52, 200)
+ logger.debug(cost)
+ return cost
+
+ def kills(self) -> int:
+ "获取击杀数"
+ img = cropimg(self.recog.gray, ((800, 30), (950, 60)))
+ img = thres2(img, 127)
+ sep = loadres("fight/kills_separator", True)
+ result = cv2.matchTemplate(img, sep, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ x = min_loc[0] + 799
+ kills = self.number(((800, 30), (x, 60)), 28, 127)
+ logger.debug(kills)
+ return kills
+
+ def skill_ready(self, x: int, y: int) -> bool:
+ """指定坐标的干员技能是否可以开启
+
+ Args:
+ x: 横坐标
+ y: 纵坐标
+ """
+ skill_ready = loadres("fight/skill_ready")
+ h, w, _ = skill_ready.shape
+ pos = self.calc.get_character_screen_pos(x, y, False, False)
+ pos = int(pos.x), int(pos.y)
+ img = cropimg(self.recog.img, sa(((-15, -168), (15, -138)), pos))
+ result = cv2.matchTemplate(img, skill_ready, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ logger.debug(f"{min_val=} {min_loc=}")
+ return min_val <= 0.2
+
+ def in_fight(self) -> bool:
+ "是否在战斗中"
+ img = cropimg(self.recog.img, ((725, 16), (797, 76)))
+ img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
+ img = cv2.inRange(img, (12, 0, 0), (16, 255, 255))
+ tpl = loadres("fight/enemy", True)
+ result = cv2.matchTemplate(img, tpl, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ value = 0.4
+ logger.debug(f"{min_val}小于{value}则判定在战斗")
+ return min_val < value
+
+ def battle_complete(self) -> bool:
+ "识别行动是否结束"
+ img = cropimg(self.recog.gray, ((87, 268), (529, 383)))
+ img = thres2(img, 200)
+ tpl = loadres("fight/complete", True)
+ result = cv2.matchTemplate(img, tpl, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ value = 0.4
+ logger.debug(f"{min_val}小于{value}则判定行动结束/胜利")
+ return min_val < value
+
+ def battle_fail(self) -> bool:
+ "识别行动是否失败"
+ img = cropimg(self.recog.gray, ((1129, 455), (1626, 531)))
+ tpl = loadres("fight/failed_text", True)
+ result = cv2.matchTemplate(img, tpl, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ value = 0.05
+ logger.debug(f"{min_val}小于{value}则判定行动失败")
+ return min_val < value # 测试时数值很低,基本为0,或有其他方法
+
+ def update_operators(self):
+ "识别可部署的干员"
+ self.recog.update()
+ y = 887
+ img = cropimg(self.recog.gray, ((0, y), (1920, 905)))
+ threshold = 0.7
+ c = loadres("fight/c", True)
+ mask = loadres("fight/c_mask", True)
+ result = cv2.matchTemplate(img, c, cv2.TM_CCOEFF_NORMED, None, mask)[0]
+ op = []
+ for i in argrelmax(result, order=50)[0]:
+ if result[i] > threshold:
+ op.append(i)
+ self.operators = {}
+ for x in op:
+ # 看最下方条的颜色判断是否正在转CD
+ bar_scope = sa(((-20, 187), (10, 190)), (x, y))
+ img = cropimg(self.recog.img, bar_scope)
+ img = cv2.inRange(img, (1, 0, 0), (3, 255, 255))
+ count = cv2.countNonZero(img)
+ logger.debug(count)
+ if count > 50:
+ continue
+
+ scope = sa(((-58, 53), (42, 153)), (x, y))
+ tpl = cropimg(self.recog.gray, scope)
+ tpl = cv2.resize(tpl, None, None, 0.5, 0.5)
+ max_score = 0
+ name = None
+ for i, img_list in avatar.items():
+ for img in img_list:
+ result = cv2.matchTemplate(img, tpl, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if max_val > max_score:
+ max_score = max_val
+ name = i
+ cost_scope = sa(((-13, 19), (30, 44)), (x, y))
+ cost = self.number(cost_scope, 25, 80)
+ self.operators[name] = {"scope": scope, "cost": cost}
+ logger.debug(self.operators)
+
+ @property
+ def action(self):
+ "下一步行动"
+ if len(self.actions) > 0:
+ return self.actions[0]
+ return None
+
+ def complete_action(self):
+ "完成当前行动"
+ self.actions.pop(0)
+
+ def toggle_play(self):
+ "切换暂停与继续"
+ self.device.tap((1800, 80))
+ sleep(1)
+ self.recog.update()
+ img = cropimg(self.recog.gray, ((740, 480), (1180, 665)))
+ img = thres2(img, 250)
+ res = loadres("fight/pause", True)
+ ssim = structural_similarity(img, res)
+ logger.debug(ssim)
+ self.playing = ssim <= 0.9
+
+ def play(self):
+ logger.info("继续")
+ while not self.playing:
+ self.toggle_play()
+
+ def pause(self):
+ logger.info("暂停")
+ while self.playing:
+ self.toggle_play()
+
+ def toggle_speed(self):
+ target = 1 if self.speed == 2 else 2
+ logger.info(f"切换至{target}倍速")
+ self.device.tap((1650, 80))
+ self.speed = target
+ self.complete_action()
+
+ def deploy(self):
+ "部署干员"
+ if "kills" in self.action and self.action["kills"] < self.kills():
+ return
+ name = self.action["name"]
+ if name not in self.operators:
+ self.update_operators()
+ return
+ if self.cost() < self.operators[name]["cost"]:
+ return
+ start = self.get_pos(self.operators[name]["scope"])
+ top = va(start, (0, -100))
+ x, y = self.action["location"]
+ pos = self.calc.get_character_screen_pos(x, y, True, False)
+ pos = int(pos.x), int(pos.y)
+ direction = self.action["direction"]
+ logger.info(f"在({x}, {y})部署{name},方向为{direction}")
+ if direction in ["Left"]:
+ dir = (-200, 0)
+ elif direction in ["Right"]:
+ dir = (200, 0)
+ elif direction in ["Up"]:
+ dir = (0, -200)
+ else:
+ dir = (0, 200)
+ dir = va(pos, dir)
+ self.device.swipe_ext([start, top, pos], [100, 500], up_wait=400)
+ sleep(0.1)
+ self.device.swipe_ext([pos, dir], [200])
+ self.operators = {}
+ self.complete_action()
+ self.location[name] = x, y
+
+ def select(self, x: int, y: int):
+ "选中干员"
+ pos = self.calc.get_character_screen_pos(x, y, False, False)
+ pos = int(pos.x), int(pos.y)
+ self.device.tap(pos)
+
+ def withdraw(self):
+ "撤下干员"
+ if "kills" in self.action and self.action["kills"] < self.kills():
+ return
+ name = self.action["name"]
+ x, y = self.location[name]
+ self.select(x, y)
+ sleep(0.5)
+ pos = self.calc.get_with_draw_screen_pos(x, y)
+ pos = int(pos.x), int(pos.y)
+ self.device.tap(pos)
+ sleep(0.5)
+ self.operators = {}
+ self.complete_action()
+
+ def use_skill(self, x, y):
+ "开技能"
+ self.select(x, y)
+ sleep(0.5)
+ pos = self.calc.get_skill_screen_pos(x, y)
+ pos = int(pos.x), int(pos.y)
+ self.device.tap(pos)
+ sleep(0.5)
+
+ def transition(self):
+ self.recog.update()
+
+ if not self.in_fight():
+ if self.battle_fail():
+ logger.warning("行动失败,请检查干员/练度")
+ return True
+ elif self.battle_complete():
+ logger.info("行动结束")
+ return True
+ else:
+ sleep(2)
+ return
+ # if self.action is None:
+ # self.sleep(10)
+ # return
+ if self.loading:
+ self.pause()
+ self.loading = False
+ self.update_operators()
+ self.play()
+ return
+
+ if self.playing:
+ for w, d in self.watching.items():
+ if w not in self.location:
+ continue
+ x, y = self.location[w]
+ if self.skill_ready(x, y):
+ self.use_skill(x, y)
+ if d["skill_usage"] == 2:
+ d["skill_times"] -= 1
+ if d["skill_times"] <= 0:
+ del self.watching[w]
+ return
+
+ if self.action["type"] == "SpeedUp":
+ self.toggle_speed()
+ elif self.action["type"] == "Deploy":
+ self.deploy()
+ elif self.action["type"] == "Retreat":
+ self.withdraw()
diff --git a/arknights_mower/solvers/base_construct.py b/arknights_mower/solvers/base_construct.py
deleted file mode 100644
index fbf27cda0..000000000
--- a/arknights_mower/solvers/base_construct.py
+++ /dev/null
@@ -1,997 +0,0 @@
-from __future__ import annotations
-
-from enum import Enum
-
-import numpy as np
-
-from ..data import base_room_list
-from ..utils import character_recognize, detector, segment
-from ..utils import typealias as tp
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
-
-
-class ArrangeOrder(Enum):
- STATUS = 1
- SKILL = 2
- FEELING = 3
- TRUST = 4
-
-arrange_order_res = {
- ArrangeOrder.STATUS: ('arrange_status', 0.1),
- ArrangeOrder.SKILL: ('arrange_skill', 0.35),
- ArrangeOrder.FEELING: ('arrange_feeling', 0.65),
- ArrangeOrder.TRUST: ('arrange_trust', 0.9),
-}
-
-
-class BaseConstructSolver(BaseSolver):
- """
- 收集基建的产物:物资、赤金、信赖
- """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
-
- def run(self, arrange: dict[str, tp.BasePlan] = None, clue_collect: bool = False, drone_room: str = None, fia_room: str = None) -> None:
- """
- :param arrange: dict(room_name: [agent,...]), 基建干员安排
- :param clue_collect: bool, 是否收取线索
- :param drone_room: str, 是否使用无人机加速
- :param fia_room: str, 是否使用菲亚梅塔恢复心情
- """
- self.arrange = arrange
- self.clue_collect = clue_collect
- self.drone_room = drone_room
- self.fia_room = fia_room
- self.todo_task = False # 基建 Todo 是否未被处理
-
- logger.info('Start: 基建')
- super().run()
-
- def transition(self) -> None:
- if self.scene() == Scene.INDEX:
- self.tap_element('index_infrastructure')
- elif self.scene() == Scene.INFRA_MAIN:
- return self.infra_main()
- elif self.scene() == Scene.INFRA_TODOLIST:
- return self.todo_list()
- elif self.scene() == Scene.INFRA_DETAILS:
- if self.find('arrange_check_in_on'):
- self.tap_element('arrange_check_in_on')
- self.back()
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_infrastructure')
- elif self.scene() == Scene.INFRA_ARRANGE_ORDER:
- self.tap_element('arrange_blue_yes')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
- else:
- raise RecognizeError('Unknown scene')
-
- def infra_main(self) -> None:
- """ 位于基建首页 """
- if self.find('control_central') is None:
- self.back()
- return
- if self.clue_collect:
- self.clue()
- self.clue_collect = False
- elif self.drone_room is not None:
- self.drone(self.drone_room)
- self.drone_room = None
- elif self.fia_room is not None:
- self.fia(self.fia_room)
- self.fia_room = None
- elif self.arrange is not None:
- self.agent_arrange(self.arrange)
- self.arrange = None
- elif not self.todo_task:
- # 处理基建 Todo
- notification = detector.infra_notification(self.recog.img)
- if notification is None:
- self.sleep(1)
- notification = detector.infra_notification(self.recog.img)
- if notification is not None:
- self.tap(notification)
- else:
- self.todo_task = True
- else:
- return True
-
- def todo_list(self) -> None:
- """ 处理基建 Todo 列表 """
- tapped = False
- trust = self.find('infra_collect_trust')
- if trust is not None:
- logger.info('基建:干员信赖')
- self.tap(trust)
- tapped = True
- bill = self.find('infra_collect_bill')
- if bill is not None:
- logger.info('基建:订单交付')
- self.tap(bill)
- tapped = True
- factory = self.find('infra_collect_factory')
- if factory is not None:
- logger.info('基建:可收获')
- self.tap(factory)
- tapped = True
- if not tapped:
- self.tap((self.recog.w*0.05, self.recog.h*0.95))
- self.todo_task = True
-
- def clue(self) -> None:
- # 一些识别时会用到的参数
- global x1, x2, x3, x4, y0, y1, y2
- x1, x2, x3, x4 = 0, 0, 0, 0
- y0, y1, y2 = 0, 0, 0
-
- logger.info('基建:线索')
-
- # 进入会客室
- self.enter_room('meeting')
-
- # 点击线索详情
- self.tap((self.recog.w*0.1, self.recog.h*0.9), interval=3)
-
- # 如果是线索交流的报告则返回
- self.find('clue_summary') and self.back()
-
- # 识别右侧按钮
- (x0, y0), (x1, y1) = self.find('clue_func', strict=True)
-
- logger.info('接收赠送线索')
- self.tap(((x0+x1)//2, (y0*3+y1)//4), interval=3, rebuild=False)
- self.tap((self.recog.w-10, self.recog.h-10), interval=3, rebuild=False)
- self.tap((self.recog.w*0.05, self.recog.h*0.95), interval=3, rebuild=False)
-
- logger.info('领取会客室线索')
- self.tap(((x0+x1)//2, (y0*5-y1)//4), interval=3)
- obtain = self.find('clue_obtain')
- if obtain is not None and self.get_color(self.get_pos(obtain, 0.25, 0.5))[0] < 20:
- self.tap(obtain, interval=2)
- if self.find('clue_full') is not None:
- self.back()
- else:
- self.back()
-
- logger.info('放置线索')
- clue_unlock = self.find('clue_unlock')
- if clue_unlock is not None:
- # 当前线索交流未开启
- self.tap_element('clue', interval=3)
-
- # 识别阵营切换栏
- self.recog_bar()
-
- # 点击总览
- self.tap(((x1*7+x2)//8, y0//2), rebuild=False)
-
- # 获得和线索视图相关的数据
- self.recog_view(only_y2=False)
-
- # 检测是否拥有全部线索
- get_all_clue = True
- for i in range(1, 8):
- # 切换阵营
- self.tap(self.switch_camp(i), rebuild=False)
-
- # 清空界面内被选中的线索
- self.clear_clue_mask()
-
- # 获得和线索视图有关的数据
- self.recog_view()
-
- # 检测该阵营线索数量为 0
- if len(self.ori_clue()) == 0:
- logger.info(f'无线索 {i}')
- get_all_clue = False
- break
-
- # 检测是否拥有全部线索
- if get_all_clue:
- for i in range(1, 8):
- # 切换阵营
- self.tap(self.switch_camp(i), rebuild=False)
-
- # 获得和线索视图有关的数据
- self.recog_view()
-
- # 放置线索
- logger.info(f'放置线索 {i}')
- self.tap(((x1+x2)//2, y1+3), rebuild=False)
-
- # 返回线索主界面
- self.tap((self.recog.w*0.05, self.recog.h*0.95), interval=3, rebuild=False)
-
- # 线索交流开启
- if clue_unlock is not None and get_all_clue:
- self.tap(clue_unlock)
- else:
- self.back(interval=2, rebuild=False)
-
- logger.info('返回基建主界面')
- self.back(interval=2)
-
- def switch_camp(self, id: int) -> tuple[int, int]:
- """ 切换阵营 """
- x = ((id+0.5) * x2 + (8-id-0.5) * x1) // 8
- y = (y0 + y1) // 2
- return x, y
-
- def recog_bar(self) -> None:
- """ 识别阵营选择栏 """
- global x1, x2, y0, y1
-
- (x1, y0), (x2, y1) = self.find('clue_nav', strict=True)
- while int(self.recog.img[y0, x1-1].max()) - int(self.recog.img[y0, x1].max()) <= 1:
- x1 -= 1
- while int(self.recog.img[y0, x2].max()) - int(self.recog.img[y0, x2-1].max()) <= 1:
- x2 += 1
- while abs(int(self.recog.img[y1+1, x1].max()) - int(self.recog.img[y1, x1].max())) <= 1:
- y1 += 1
- y1 += 1
-
- logger.debug(f'recog_bar: x1:{x1}, x2:{x2}, y0:{y0}, y1:{y1}')
-
- def recog_view(self, only_y2: bool = True) -> None:
- """ 识别另外一些和线索视图有关的数据 """
- global x1, x2, x3, x4, y0, y1, y2
-
- # y2: 线索底部
- y2 = self.recog.h
- while self.recog.img[y2-1, x1:x2].ptp() <= 24:
- y2 -= 1
- if only_y2:
- logger.debug(f'recog_view: y2:{y2}')
- return y2
- # x3: 右边黑色 mask 边缘
- x3 = self.recog_view_mask_right()
- # x4: 用来区分单个线索
- x4 = (54 * x1 + 25 * x2) // 79
-
- logger.debug(f'recog_view: y2:{y2}, x3:{x3}, x4:{x4}')
-
- def recog_view_mask_right(self) -> int:
- """ 识别线索视图中右边黑色 mask 边缘的位置 """
- x3 = x2
- while True:
- max_abs = 0
- for y in range(y1, y2):
- max_abs = max(max_abs,
- abs(int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0])))
- if max_abs <= 5:
- x3 -= 1
- else:
- break
- flag = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0]) == max_abs:
- flag = True
- if not flag:
- self.tap(((x1+x2)//2, y1+10), rebuild=False)
- x3 = x2
- while True:
- max_abs = 0
- for y in range(y1, y2):
- max_abs = max(max_abs,
- abs(int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0])))
- if max_abs <= 5:
- x3 -= 1
- else:
- break
- flag = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0]) == max_abs:
- flag = True
- if not flag:
- x3 = None
- return x3
-
- def get_clue_mask(self) -> None:
- """ 界面内是否有被选中的线索 """
- try:
- mask = []
- for y in range(y1, y2):
- if int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0]) > 20 and np.ptp(self.recog.img[y, x3-2]) == 0:
- mask.append(y)
- if len(mask) > 0:
- logger.debug(np.average(mask))
- return np.average(mask)
- else:
- return None
- except Exception as e:
- raise RecognizeError(e)
-
- def clear_clue_mask(self) -> None:
- """ 清空界面内被选中的线索 """
- try:
- while True:
- mask = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3-1, 0]) - int(self.recog.img[y, x3-2, 0]) > 20 and np.ptp(self.recog.img[y, x3-2]) == 0:
- self.tap((x3-2, y+1), rebuild=True)
- mask = True
- break
- if mask:
- continue
- break
- except Exception as e:
- raise RecognizeError(e)
-
- def ori_clue(self):
- """ 获取界面内有多少线索 """
- clues = []
- y3 = y1
- status = -2
- for y in range(y1, y2):
- if self.recog.img[y, x4-5:x4+5].max() < 192:
- if status == -1:
- status = 20
- if status > 0:
- status -= 1
- if status == 0:
- status = -2
- clues.append(segment.get_poly(x1, x2, y3, y-20))
- y3 = y-20+5
- else:
- status = -1
- if status != -2:
- clues.append(segment.get_poly(x1, x2, y3, y2))
-
- # 忽视一些只有一半的线索
- clues = [x.tolist() for x in clues if x[1][1] - x[0][1] >= self.recog.h / 5]
- logger.debug(clues)
- return clues
-
- def enter_room(self, room: str) -> tp.Rectangle:
- """ 获取房间的位置并进入 """
-
- # 获取基建各个房间的位置
- base_room = segment.base(self.recog.img, self.find('control_central', strict=True))
-
- # 将画面外的部分删去
- room = base_room[room]
- for i in range(4):
- room[i, 0] = max(room[i, 0], 0)
- room[i, 0] = min(room[i, 0], self.recog.w)
- room[i, 1] = max(room[i, 1], 0)
- room[i, 1] = min(room[i, 1], self.recog.h)
-
- # 点击进入
- self.tap(room[0], interval=3)
- while self.find('control_central') is not None:
- self.tap(room[0], interval=3)
-
- def drone(self, room: str):
- logger.info('基建:无人机加速')
-
- # 点击进入该房间
- self.enter_room(room)
-
- # 进入房间详情
- self.tap((self.recog.w*0.05, self.recog.h*0.95), interval=3)
-
- accelerate = self.find('factory_accelerate')
- if accelerate:
- logger.info('制造站加速')
- self.tap(accelerate)
- self.tap_element('all_in')
- self.tap(accelerate, y_rate=1)
-
- else:
- accelerate = self.find('bill_accelerate')
- while accelerate:
- logger.info('贸易站加速')
- self.tap(accelerate)
- self.tap_element('all_in')
- self.tap((self.recog.w*0.75, self.recog.h*0.8), interval=3) # relative position 0.75, 0.8
-
- st = accelerate[1] # 起点
- ed = accelerate[0] # 终点
- # 0.95, 1.05 are offset compensations
- self.swipe_noinertia(st, (ed[0]*0.95-st[0]*1.05, 0), rebuild=True)
- accelerate = self.find('bill_accelerate')
-
- logger.info('返回基建主界面')
- self.back(interval=2, rebuild=False)
- self.back(interval=2)
-
- def get_arrange_order(self) -> ArrangeOrder:
- best_score, best_order = 0, None
- for order in ArrangeOrder:
- score = self.recog.score(arrange_order_res[order][0])
- if score is not None and score[0] > best_score:
- best_score, best_order = score[0], order
- # if best_score < 0.6:
- # raise RecognizeError
- logger.debug((best_score, best_order))
- return best_order
-
- def switch_arrange_order(self, order: ArrangeOrder) -> None:
- self.tap_element(arrange_order_res[order][0], x_rate=arrange_order_res[order][1], judge=False)
-
- def arrange_order(self, order: ArrangeOrder) -> None:
- if self.get_arrange_order() != order:
- self.switch_arrange_order(order)
-
- def choose_agent(self, agent: list[str], skip_free: int = 0, order: ArrangeOrder = None) -> None:
- """
- :param order: ArrangeOrder, 选择干员时右上角的排序功能
- """
- logger.info(f'安排干员:{agent}')
- logger.debug(f'skip_free: {skip_free}')
- h, w = self.recog.h, self.recog.w
- first_time = True
-
- # 在 agent 中 'Free' 表示任意空闲干员
- free_num = agent.count('Free')
- agent = set(agent) - set(['Free'])
-
- # 安排指定干员
- if len(agent):
-
- if not first_time:
- # 滑动到最左边
- self.sleep(interval=0.5, rebuild=False)
- for _ in range(9):
- self.swipe_only((w//2, h//2), (w//2, 0), interval=0.5)
- self.swipe((w//2, h//2), (w//2, 0), interval=3, rebuild=False)
- else:
- # 第一次进入按技能排序
- if order is not None:
- self.arrange_order(order)
- first_time = False
-
- checked = set() # 已经识别过的干员
- pre = set() # 上次识别出的干员
- error_count = 0
-
- while len(agent):
- try:
- # 识别干员
- ret = character_recognize.agent(self.recog.img) # 返回的顺序是从左往右从上往下
- except RecognizeError as e:
- error_count += 1
- if error_count < 3:
- logger.debug(e)
- self.sleep(3)
- else:
- raise e
- continue
-
- # 提取识别出来的干员的名字
- agent_name = set([x[0] for x in ret])
- if agent_name == pre:
- error_count += 1
- if error_count >= 3:
- logger.warning(f'未找到干员:{list(agent)}')
- break
- else:
- pre = agent_name
-
- # 更新已经识别过的干员
- checked |= agent_name
-
- # 如果出现了需要的干员则选择
- # 优先安排菲亚梅塔
- if '菲亚梅塔' in agent:
- if '菲亚梅塔' in agent_name:
- for y in ret:
- if y[0] == '菲亚梅塔':
- self.tap((y[1][0]), interval=0, rebuild=False)
- break
- agent.remove('菲亚梅塔')
-
- # 如果菲亚梅塔是 the only one
- if len(agent) == 0:
- break
- # 否则滑动到最左边
- self.sleep(interval=0.5, rebuild=False)
- for _ in range(9):
- self.swipe_only((w//2, h//2), (w//2, 0), interval=0.5)
- self.swipe((w//2, h//2), (w//2, 0), interval=3, rebuild=False)
-
- # reset the statuses and cancel the rightward-swiping
- checked = set()
- pre = set()
- error_count = 0
- continue
-
- else:
- for y in ret:
- name = y[0]
- if name in agent_name & agent:
- self.tap((y[1][0]), interval=0, rebuild=False)
- agent.remove(name)
- # for name in agent_name & agent:
- # for y in ret:
- # if y[0] == name:
- # self.tap((y[1][0]), interval=0, rebuild=False)
- # break
- # agent.remove(name)
-
- # 如果已经完成选择则退出
- if len(agent) == 0:
- break
-
- st = ret[-2][1][2] # 起点
- ed = ret[0][1][1] # 终点
- self.swipe_noinertia(st, (ed[0]-st[0], 0))
-
- # 安排空闲干员
- if free_num:
-
- if not first_time:
- # 滑动到最左边
- self.sleep(interval=0.5, rebuild=False)
- for _ in range(9):
- self.swipe_only((w//2, h//2), (w//2, 0), interval=0.5)
- self.swipe((w//2, h//2), (w//2, 0), interval=3, rebuild=False)
- else:
- # 第一次进入按技能排序
- if order is not None:
- self.arrange_order(order)
- first_time = False
-
- error_count = 0
-
- while free_num:
- try:
- # 识别空闲干员
- ret, st, ed = segment.free_agent(self.recog.img) # 返回的顺序是从左往右从上往下
- except RecognizeError as e:
- error_count += 1
- if error_count < 3:
- logger.debug(e)
- self.sleep(3)
- else:
- raise e
- continue
-
- while free_num and len(ret):
- if skip_free > 0:
- skip_free -= 1
- else:
- self.tap(ret[0], interval=0, rebuild=False)
- free_num -= 1
- ret = ret[1:]
-
- # 如果已经完成选择则退出
- if free_num == 0:
- break
-
- self.swipe_noinertia(st, (ed[0]-st[0], 0))
-
- def agent_arrange(self, plan: tp.BasePlan) -> None:
- """ 基建排班 """
- logger.info('基建:排班')
-
- # 进入进驻总览
- self.tap_element('infra_overview', interval=2)
-
- logger.info('安排干员工作……')
- idx = 0
- room_total = len(base_room_list)
- need_empty = set(list(plan.keys()))
- while idx < room_total:
- ret, switch, mode = segment.worker(self.recog.img)
- if len(ret) == 0:
- raise RecognizeError('未识别到进驻总览中的房间列表')
-
- # 关闭撤下干员按钮
- if mode:
- self.tap((switch[0][0]+5, switch[0][1]+5), rebuild=False)
- continue
-
- if room_total-idx < len(ret):
- # 已经滑动到底部
- ret = ret[-(room_total-idx):]
-
- for block in ret:
- if base_room_list[idx] in need_empty:
- need_empty.remove(base_room_list[idx])
- # 对这个房间进行换班
- finished = len(plan[base_room_list[idx]]) == 0
- skip_free = 0
- error_count = 0
- while not finished:
- x = (7*block[0][0]+3*block[2][0])//10
- y = (block[0][1]+block[2][1])//2
- self.tap((x, y))
-
- # 若不是空房间,则清空工作中的干员
- if self.find('arrange_empty_room') is None:
- if self.find('arrange_clean') is not None:
- self.tap_element('arrange_clean')
- else:
- # 对于只有一个干员的房间,没有清空按钮,需要点击干员清空
- self.tap((self.recog.w*0.38, self.recog.h*0.3), interval=0)
-
- try:
- if base_room_list[idx].startswith('dormitory'):
- default_order = ArrangeOrder.FEELING
- else:
- default_order = ArrangeOrder.SKILL
- self.choose_agent(
- plan[base_room_list[idx]], skip_free, default_order)
- except RecognizeError as e:
- error_count += 1
- if error_count >= 3:
- raise e
- # 返回基建干员进驻总览
- self.recog.update()
- while self.scene() not in [Scene.INFRA_ARRANGE, Scene.INFRA_MAIN] and self.scene() // 100 != 1:
- pre_scene = self.scene()
- self.back(interval=3)
- if self.scene() == pre_scene:
- break
- if self.scene() != Scene.INFRA_ARRANGE:
- raise e
- continue
- self.recog.update()
- self.tap_element(
- 'confirm_blue', detected=True, judge=False, interval=3)
- if self.scene() == Scene.INFRA_ARRANGE_CONFIRM:
- x = self.recog.w // 3 * 2 # double confirm
- y = self.recog.h - 10
- self.tap((x, y), rebuild=True)
- finished = True
- while self.scene() == Scene.CONNECTING:
- self.sleep(3)
- idx += 1
-
- # 换班结束
- if idx == room_total or len(need_empty) == 0:
- break
- block = ret[-1]
- top = switch[2][1]
- self.swipe_noinertia(tuple(block[1]), (0, top-block[1][1]))
-
- logger.info('返回基建主界面')
- self.back()
-
- def choose_agent_in_order(self, agent: list[str], exclude: list[str] = None, exclude_checked_in: bool = False, dormitory: bool = False):
- """
- 按照顺序选择干员,只选择未在工作、未注意力涣散、未在休息的空闲干员
-
- :param agent: 指定入驻干员列表
- :param exclude: 排除干员列表,不选择这些干员
- :param exclude_checked_in: 是否仅选择未进驻干员
- :param dormitory: 是否是入驻宿舍,如果不是,则不选择注意力涣散的干员
- """
- if exclude is None:
- exclude = []
- if exclude_checked_in:
- self.tap_element('arrange_order_options')
- self.tap_element('arrange_non_check_in')
- self.tap_element('arrange_blue_yes')
- self.tap_element('arrange_clean')
-
- h, w = self.recog.h, self.recog.w
- first_time = True
- far_left = True
- _free = None
- idx = 0
- while idx < len(agent):
- logger.info('寻找干员: %s', agent[idx])
- found = 0
- while found == 0:
- ret = character_recognize.agent(self.recog.img)
- # 'Free'代表占位符,选择空闲干员
- if agent[idx] == 'Free':
- for x in ret:
- status_coord = x[1].copy()
- status_coord[0, 1] -= 0.147*self.recog.h
- status_coord[2, 1] -= 0.135*self.recog.h
-
- room_coord = x[1].copy()
- room_coord[0, 1] -= 0.340*self.recog.h
- room_coord[2, 1] -= 0.340*self.recog.h
-
- if x[0] not in agent and x[0] not in exclude:
- # 不选择已进驻的干员,如果非宿舍则进一步不选择精神涣散的干员
- if not (self.find('agent_on_shift', scope=(status_coord[0], status_coord[2]))
- or self.find('agent_resting', scope=(status_coord[0], status_coord[2]))
- or self.find('agent_in_dormitory', scope=(room_coord[0], room_coord[2]))
- or (not dormitory and self.find('agent_distracted', scope=(status_coord[0], status_coord[2])))):
- self.tap(x[1], x_rate=0.5, y_rate=0.5, interval=0)
- agent[idx] = x[0]
- _free = x[0]
- found = 1
- break
-
- elif agent[idx] != 'Free':
- for x in ret:
- if x[0] == agent[idx]:
- self.tap(x[1], x_rate=0.5, y_rate=0.5, interval=0)
- found = 1
- break
-
- if found == 1:
- idx += 1
- first_time = True
- break
-
- if first_time and not far_left and agent[idx] != 'Free':
- # 如果是寻找这位干员目前为止的第一次滑动, 且目前不是最左端,则滑动到最左端
- self.sleep(interval=0.5, rebuild=False)
- for _ in range(9):
- self.swipe_only((w//2, h//2), (w//2, 0), interval=0.5)
- self.swipe((w//2, h//2), (w//2, 0), interval=3, rebuild=True)
- far_left = True
- first_time = False
- else:
- st = ret[-2][1][2] # 起点
- ed = ret[0][1][1] # 终点
- self.swipe_noinertia(st, (ed[0]-st[0], 0), rebuild=True)
- far_left = False
- first_time = False
- self.recog.update()
- return _free
-
- def fia(self, room: str):
- """
- 使用菲亚梅塔恢复指定房间心情最差的干员的心情,同时保持工位顺序不变
- 目前仅支持制造站、贸易站、发电站 (因为其他房间在输入命令时较为繁琐,且需求不大)
- 使用前需要菲亚梅塔位于任意一间宿舍
- """
- # 基建干员选择界面,导航栏四个排序选项的相对位置
- BY_STATUS = [0.622, 0.05] # 按工作状态排序
- BY_SKILL = [0.680, 0.05] # 按技能排序
- BY_EMO = [0.755, 0.05] # 按心情排序
- BY_TRUST = [0.821, 0.05] # 按信赖排序
-
- logger.info('基建:使用菲亚梅塔恢复心情')
- self.tap_element('infra_overview', interval=2)
-
- logger.info('查询菲亚梅塔状态')
- idx = 0
- room_total = len(base_room_list)
- fia_resting = fia_full = None
- while idx < room_total:
- ret, switch, _ = segment.worker(self.recog.img)
- if room_total-idx < len(ret):
- # 已经滑动到底部
- ret = ret[-(room_total-idx):]
-
- for block in ret:
- if 'dormitory' in base_room_list[idx]:
- fia_resting = self.find('fia_resting', scope=(block[0], block[2])) \
- or self.find('fia_resting_elite2', scope=(block[0], block[2]))
- if fia_resting:
- logger.info('菲亚梅塔还在休息')
- break
-
- fia_full = self.find('fia_full', scope=(block[0], block[2])) \
- or self.find('fia_full_elite2', scope=(block[0], block[2]))
- if fia_full:
- fia_full = base_room_list[idx]
- break
- idx += 1
-
- if fia_full or fia_resting:
- break
-
- block = ret[-1]
- top = switch[2][1]
- self.swipe_noinertia(tuple(block[1]), (0, top-block[1][1]), rebuild=True)
-
- if not fia_resting and not fia_full:
- logger.warning('未找到菲亚梅塔,使用本功能前请将菲亚梅塔置于宿舍!')
-
- elif fia_full:
- logger.info('菲亚梅塔心情已满,位于%s', fia_full)
- logger.info('查询指定房间状态')
- self.back(interval=2)
- self.enter_room(room)
- # 进入进驻详情
- if not self.find('arrange_check_in_on'):
- self.tap_element('arrange_check_in', interval=2, rebuild=False)
- self.tap((self.recog.w*0.82, self.recog.h*0.25), interval=2)
- # 确保按工作状态排序 防止出错
- self.tap((self.recog.w*BY_TRUST[0], self.recog.h*BY_TRUST[1]), interval=0)
- self.tap((self.recog.w*BY_STATUS[0], self.recog.h*BY_STATUS[1]), interval=0.1)
- # 记录房间中的干员及其工位顺序
- ret = character_recognize.agent(self.recog.img)
- on_shift_agents = []
- for x in ret:
- x[1][0, 1] -= 0.147*self.recog.h
- x[1][2, 1] -= 0.135*self.recog.h
- if self.find('agent_on_shift', scope=(x[1][0], x[1][2])) \
- or self.find('agent_distracted', scope=(x[1][0], x[1][2])):
- self.tap(x[1], x_rate=0.5, y_rate=0.5, interval=0)
- on_shift_agents.append(x[0])
- if len(on_shift_agents) == 0:
- logger.warning('该房间没有干员在工作')
- return
- logger.info('房间内的干员顺序为: %s', on_shift_agents)
-
- # 确保按心情升序排列
- self.tap((self.recog.w*BY_TRUST[0], self.recog.h*BY_TRUST[1]), interval=0)
- self.tap((self.recog.w*BY_EMO[0], self.recog.h*BY_EMO[1]), interval=0)
- self.tap((self.recog.w*BY_EMO[0], self.recog.h*BY_EMO[1]), interval=0.1)
- # 寻找这个房间里心情最低的干员,
- _temp_on_shift_agents = on_shift_agents.copy()
- while 'Free' not in _temp_on_shift_agents:
- ret = character_recognize.agent(self.recog.img)
- for x in ret:
- if x[0] in _temp_on_shift_agents:
- # 用占位符替代on_shift_agents中这个agent
- _temp_on_shift_agents[_temp_on_shift_agents.index(x[0])] = 'Free'
- logger.info('房间内心情最差的干员为: %s', x[0])
- _recover = x[0]
- break
- if 'Free' in _temp_on_shift_agents:
- break
-
- st = ret[-2][1][2] # 起点
- ed = ret[0][1][1] # 终点
- self.swipe_noinertia(st, (ed[0]-st[0], 0), rebuild=True)
- self.back(interval=2)
- self.back(interval=2)
-
- logger.info('进入菲亚梅塔所在宿舍,为%s恢复心情', _recover)
- self.enter_room(fia_full)
- # 进入进驻详情
- if not self.find('arrange_check_in_on'):
- self.tap_element('arrange_check_in', interval=2, rebuild=False)
- self.tap((self.recog.w*0.82, self.recog.h*0.25), interval=2)
- # 选择待恢复干员和菲亚梅塔
- rest_agents = [_recover, '菲亚梅塔']
- self.choose_agent_in_order(rest_agents, exclude_checked_in=False)
- self.tap_element('confirm_blue', detected=True, judge=False, interval=1)
- # double confirm
- if self.scene() == Scene.INFRA_ARRANGE_CONFIRM:
- x = self.recog.w // 3 * 2
- y = self.recog.h - 10
- self.tap((x, y), rebuild=True)
- while self.scene() == Scene.CONNECTING:
- self.sleep(3)
-
- logger.info('恢复完毕,填满宿舍')
- rest_agents = '菲亚梅塔 Free Free Free Free'.split()
- self.tap((self.recog.w*0.82, self.recog.h*0.25), interval=2)
- self.choose_agent_in_order(rest_agents, exclude=[_recover], dormitory=True)
- self.tap_element('confirm_blue', detected=True, judge=False, interval=3)
- while self.scene() == Scene.CONNECTING:
- self.sleep(3)
-
- logger.info('恢复原职')
- self.back(interval=2)
- self.enter_room(room)
- if not self.find('arrange_check_in_on'):
- self.tap_element('arrange_check_in', interval=2, rebuild=False)
- self.tap((self.recog.w*0.82, self.recog.h*0.25), interval=2)
- self.choose_agent_in_order(on_shift_agents)
- self.tap_element('confirm_blue', detected=True, judge=False, interval=3)
- while self.scene() == Scene.CONNECTING:
- self.sleep(3)
- self.back(interval=2)
-
- # def clue_statis(self):
-
- # clues = {'all': {}, 'own': {}}
-
- # self.recog_bar()
- # self.tap(((x1*7+x2)//8, y0//2), rebuild=False)
- # self.tap(((x1*7.5+x2*0.5)//8, (y0+y1)//2), rebuild=False)
- # self.recog_view(only_y2=False)
-
- # if x3 is None:
- # return clues
-
- # for i in range(1, 8):
-
- # self.tap((((i+0.5)*x2+(8-i-0.5)*x1)//8, (y0+y1)//2), rebuild=False)
- # self.clear_clue_mask()
- # self.recog_view()
-
- # count = 0
- # if y2 < self.recog.h - 20:
- # count = len(self.ori_clue())
- # else:
- # while True:
- # restart = False
- # count = 0
- # ret = self.ori_clue()
- # while True:
-
- # y4 = 0
- # for poly in ret:
- # count += 1
- # y4 = poly[0, 1]
-
- # self.tap((x4, y4+10), rebuild=False)
- # self.device.swipe([(x4, y4), (x4, y1+10), (0, y1+10)], duration=(y4-y1-10)*3)
- # self.sleep(1, rebuild=False)
-
- # mask = self.get_clue_mask()
- # if mask is not None:
- # self.clear_clue_mask()
- # ret = self.ori_clue()
-
- # if mask is None or not (ret[0][0, 1] <= mask <= ret[-1][1, 1]):
- # # 漂移了的话
- # restart = True
- # break
-
- # if ret[0][0, 1] <= mask <= ret[0][1, 1]:
- # count -= 1
- # continue
- # else:
- # for poly in ret:
- # if mask < poly[0, 1]:
- # count += 1
- # break
-
- # if restart:
- # self.swipe((x4, y1+10), (0, 1000),
- # duration=500, interval=3, rebuild=False)
- # continue
- # break
-
- # clues['all'][i] = count
-
- # self.tap(((x1+x2)//2, y0//2), rebuild=False)
-
- # for i in range(1, 8):
- # self.tap((((i+0.5)*x2+(8-i-0.5)*x1)//8, (y0+y1)//2), rebuild=False)
-
- # self.clear_clue_mask()
- # self.recog_view()
-
- # count = 0
- # if y2 < self.recog.h - 20:
- # count = len(self.ori_clue())
- # else:
- # while True:
- # restart = False
- # count = 0
- # ret = self.ori_clue()
- # while True:
-
- # y4 = 0
- # for poly in ret:
- # count += 1
- # y4 = poly[0, 1]
-
- # self.tap((x4, y4+10), rebuild=False)
- # self.device.swipe([(x4, y4), (x4, y1+10), (0, y1+10)], duration=(y4-y1-10)*3)
- # self.sleep(1, rebuild=False)
-
- # mask = self.get_clue_mask()
- # if mask is not None:
- # self.clear_clue_mask()
- # ret = self.ori_clue()
-
- # if mask is None or not (ret[0][0, 1] <= mask <= ret[-1][1, 1]):
- # # 漂移了的话
- # restart = True
- # break
-
- # if ret[0][0, 1] <= mask <= ret[0][1, 1]:
- # count -= 1
- # continue
- # else:
- # for poly in ret:
- # if mask < poly[0, 1]:
- # count += 1
- # break
-
- # if restart:
- # self.swipe((x4, y1+10), (0, 1000),
- # duration=500, interval=3, rebuild=False)
- # continue
- # break
-
- # clues['own'][i] = count
-
- # return clues
diff --git a/arknights_mower/solvers/base_mixin.py b/arknights_mower/solvers/base_mixin.py
new file mode 100644
index 000000000..4698fbaa3
--- /dev/null
+++ b/arknights_mower/solvers/base_mixin.py
@@ -0,0 +1,358 @@
+import lzma
+import pickle
+from datetime import datetime, timedelta
+
+import cv2
+import numpy as np
+
+from arknights_mower import __rootdir__
+from arknights_mower.utils import rapidocr, segment
+from arknights_mower.utils.character_recognize import operator_list
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.image import cropimg, loadres, thres2
+from arknights_mower.utils.log import logger
+
+with lzma.open(f"{__rootdir__}/models/operator_room.model", "rb") as f:
+ OP_ROOM = pickle.loads(f.read())
+
+kernel = np.ones((12, 12), np.uint8)
+
+
+class BaseMixin:
+ def detect_arrange_order(self):
+ name_list = ["工作状态", "技能", "心情", "信赖值"]
+ x_list = (1196, 1320, 1445, 1572)
+ y = 70
+ hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ mask = cv2.inRange(hsv, (99, 200, 0), (100, 255, 255))
+ for idx, x in enumerate(x_list):
+ if np.count_nonzero(mask[y : y + 3, x : x + 5]):
+ return (name_list[idx], True)
+ if np.count_nonzero(mask[y + 10 : y + 13, x : x + 5]):
+ return (name_list[idx], False)
+
+ def switch_arrange_order(self, name, ascending=False):
+ name_x = {"工作状态": 1197, "技能": 1322, "心情": 1447, "信赖值": 1575}
+ if isinstance(name, int):
+ name = list(name_x.keys())[name - 1]
+ if isinstance(ascending, str):
+ ascending = ascending == "true"
+ name_y = 60
+ self.tap((name_x[name], name_y), interval=0.5)
+ while True:
+ n, s = self.detect_arrange_order()
+ if n == name and s == ascending:
+ break
+ self.tap((name_x[name], name_y), interval=0.5)
+
+ def scan_agent(self, agent: list[str], error_count=0, max_agent_count=-1):
+ try:
+ # 识别干员
+ while self.find("connecting"):
+ logger.info("等待网络连接")
+ self.sleep()
+ # 返回的顺序是从左往右从上往下
+ ret = operator_list(self.recog.img)
+ # 提取识别出来的干员的名字
+ select_name = []
+ for name, scope in ret:
+ if name in agent:
+ select_name.append(name)
+ # self.get_agent_detail((y[1][0]))
+ self.tap(scope, interval=0)
+ agent.remove(name)
+ # 如果是按照个数选择 Free
+ if max_agent_count != -1:
+ if len(select_name) >= max_agent_count:
+ return select_name, ret
+ return select_name, ret
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ error_count += 1
+ if error_count < 3:
+ self.sleep(3)
+ return self.scan_agent(agent, error_count, max_agent_count)
+ else:
+ raise e
+
+ def verify_agent(self, agent: list[str], error_count=0, max_agent_count=-1):
+ try:
+ # 识别干员
+ while self.find("connecting"):
+ logger.info("等待网络连接")
+ self.sleep()
+ ret = operator_list(self.recog.img) # 返回的顺序是从左往右从上往下
+ # 提取识别出来的干员的名字
+ index = 0
+ for name, scope in ret:
+ if index >= len(agent):
+ return True
+ if name != agent[index]:
+ return False
+ index += 1
+ return True
+ except Exception as e:
+ logger.exception(e)
+ error_count += 1
+ self.switch_arrange_order("技能")
+ if error_count < 3:
+ self.sleep(3)
+ return self.verify_agent(agent, error_count, max_agent_count)
+ else:
+ raise e
+
+ def swipe_left(self, right_swipe):
+ if right_swipe > 3:
+ self.detail_filter(控制中枢=True)
+ self.detail_filter(控制中枢=False)
+ else:
+ swipe_time = 2 if right_swipe == 3 else right_swipe
+ for i in range(swipe_time):
+ self.swipe_noinertia((650, 540), (1900, 0))
+ return 0
+
+ def detail_filter(self, **kwargs):
+ if kwargs:
+ text = ",".join(
+ f"{'打开' if value else '关闭'}{label}筛选"
+ for label, value in kwargs.items()
+ )
+ text += ",关闭其余筛选"
+ logger.info(text)
+ else:
+ logger.info("关闭所有筛选")
+
+ labels = [
+ "未进驻",
+ "产出设施",
+ "功能设施",
+ "自定义设施",
+ "控制中枢",
+ "生产类后勤",
+ "功能类后勤",
+ "恢复类后勤",
+ ]
+ label_x = (560, 815, 1070, 1330)
+ label_y = (540, 645)
+
+ label_pos = []
+ for y in label_y:
+ for x in label_x:
+ label_pos.append((x, y))
+
+ label_pos_map = dict(zip(labels, label_pos))
+ target_state = dict(zip(labels, [False] * len(labels)))
+ target_state.update(kwargs)
+
+ filter_pos = (self.recog.w * 0.95, self.recog.h * 0.05)
+ self.tap(filter_pos)
+
+ err_cnt = 0
+ while not self.find("arrange_order_options_scene"):
+ self.ctap(filter_pos)
+ err_cnt += 1
+ if err_cnt > 3:
+ raise Exception("未进入筛选页面")
+
+ for label, pos in label_pos_map.items():
+ current_state = self.get_color(pos)[2] > 100
+ if target_state[label] != current_state:
+ self.tap(pos, interval=0.1)
+
+ self.recog.update()
+ confirm_pos = (self.recog.w * 0.8, self.recog.h * 0.8)
+ self.tap(confirm_pos)
+
+ err_cnt = 0
+ while self.find("arrange_order_options_scene"):
+ self.ctap(confirm_pos)
+ err_cnt += 1
+ if err_cnt > 3:
+ raise Exception("筛选确认失败")
+
+ def detect_room_number(self, img) -> int:
+ score = []
+ for i in range(1, 5):
+ digit = loadres(f"room/{i}")
+ result = cv2.matchTemplate(img, digit, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(max_val)
+ return score.index(max(score)) + 1
+
+ def detect_room(self) -> str:
+ color_map = {
+ "制造站": 25,
+ "贸易站": 99,
+ "发电站": 36,
+ "训练室": 178,
+ "加工站": 32,
+ }
+ img = cropimg(self.recog.img, ((568, 18), (957, 95)))
+ hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
+ colored_room = None
+ for room, color in color_map.items():
+ mask = cv2.inRange(hsv, (color - 1, 0, 0), (color + 2, 255, 255))
+ if cv2.countNonZero(mask) > 1000:
+ colored_room = room
+ break
+ if colored_room in ["制造站", "贸易站", "发电站"]:
+ digit_1 = cropimg(img, ((211, 24), (232, 54)))
+ digit_2 = cropimg(img, ((253, 24), (274, 54)))
+ digit_1 = self.detect_room_number(digit_1)
+ digit_2 = self.detect_room_number(digit_2)
+ logger.debug(f"{colored_room}B{digit_1}0{digit_2}")
+ return f"room_{digit_1}_{digit_2}"
+ elif colored_room == "训练室":
+ logger.debug("训练室B305")
+ return "train"
+ elif colored_room == "加工站":
+ logger.debug("加工站B105")
+ return "factory"
+ white_room = ["central", "dormitory", "meeting", "contact"]
+ score = []
+ for room in white_room:
+ tpl = loadres(f"room/{room}")
+ result = cv2.matchTemplate(img, tpl, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(max_val)
+ room = white_room[score.index(max(score))]
+ if room == "central":
+ logger.debug("控制中枢")
+ elif room == "dormitory":
+ digit = cropimg(img, ((174, 24), (195, 54)))
+ digit = self.detect_room_number(digit)
+ if digit == 4:
+ logger.debug("宿舍B401")
+ else:
+ logger.debug(f"宿舍B{digit}04")
+ return f"dormitory_{digit}"
+ elif room == "meeting":
+ logger.debug("会客室1F02")
+ else:
+ logger.debug("办公室B205")
+ return room
+
+ def enter_room(self, room):
+ """从基建首页进入房间"""
+ for enter_times in range(3):
+ for retry_times in range(10):
+ if pos := self.find("control_central"):
+ _room = segment.base(self.recog.img, pos)[room]
+ for i in range(4):
+ _room[i, 0] = max(_room[i, 0], 0)
+ _room[i, 0] = min(_room[i, 0], self.recog.w)
+ _room[i, 1] = max(_room[i, 1], 0)
+ _room[i, 1] = min(_room[i, 1], self.recog.h)
+ self.tap(_room)
+ elif self.detect_room() == room:
+ return
+ else:
+ self.sleep()
+ if not pos:
+ self.back_to_infrastructure()
+ raise Exception("未成功进入房间")
+
+ def double_read_time(self, cord, upperLimit=None, use_digit_reader=False):
+ self.recog.update()
+ time_in_seconds = self.read_time(cord, upperLimit, use_digit_reader)
+ if time_in_seconds is None:
+ return datetime.now()
+ execute_time = datetime.now() + timedelta(seconds=(time_in_seconds))
+ return execute_time
+
+ def read_accurate_mood(self, img):
+ try:
+ img = thres2(img, 200)
+ return cv2.countNonZero(img) * 24 / 310
+ except Exception as e:
+ logger.exception(e)
+ return 24
+
+ def detect_product_complete(self):
+ for product in ["gold", "exp", "lmd", "ori", "oru", "trust"]:
+ if pos := self.find(
+ f"infra_{product}_complete",
+ scope=((1230, 0), (1920, 1080)),
+ score=0.1,
+ ):
+ return pos
+
+ def read_operator_in_room(self, img):
+ img = thres2(img, 200)
+ img = cv2.copyMakeBorder(img, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,))
+ dilation = cv2.dilate(img, kernel, iterations=1)
+ contours, _ = cv2.findContours(dilation, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
+ rect = map(lambda c: cv2.boundingRect(c), contours)
+ x, y, w, h = sorted(rect, key=lambda c: c[0])[0]
+ img = img[y : y + h, x : x + w]
+ tpl = np.zeros((46, 265), dtype=np.uint8)
+ tpl[: img.shape[0], : img.shape[1]] = img
+ tpl = cv2.copyMakeBorder(tpl, 2, 2, 2, 2, cv2.BORDER_CONSTANT, None, (0,))
+ max_score = 0
+ best_operator = None
+ for operator, template in OP_ROOM.items():
+ result = cv2.matchTemplate(tpl, template, cv2.TM_CCORR_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if max_val > max_score:
+ max_score = max_val
+ best_operator = operator
+ return best_operator
+
+ def read_screen(self, img, type="mood", limit=24, cord=None):
+ if cord is not None:
+ img = cropimg(img, cord)
+ if type == "name":
+ img = cropimg(img, ((169, 22), (513, 80)))
+ return self.read_operator_in_room(img)
+ try:
+ ret = rapidocr.engine(img, use_det=False, use_cls=False, use_rec=True)[0]
+ logger.debug(ret)
+ if not ret or not ret[0][0]:
+ raise Exception("识别失败")
+ ret = ret[0][0]
+ if "mood" in type:
+ if (f"/{limit}") in ret:
+ ret = ret.replace(f"/{limit}", "")
+ if len(ret) > 0:
+ if "." in ret:
+ ret = ret.replace(".", "")
+ return int(ret)
+ else:
+ return -1
+ elif "time" in type:
+ if "." in ret:
+ ret = ret.replace(".", ":")
+ return ret.strip()
+ else:
+ return ret
+ except Exception as e:
+ logger.exception(e)
+ return limit + 1
+
+ def read_time(self, cord, upperlimit, error_count=0, use_digit_reader=False):
+ # 刷新图片
+ self.recog.update()
+ try:
+ if use_digit_reader:
+ time_str = self.digit_reader.get_time(self.recog.gray)
+ else:
+ time_str = self.read_screen(self.recog.img, type="time", cord=cord)
+ h, m, s = str(time_str).split(":")
+ if int(m) > 60 or int(s) > 60:
+ raise Exception("读取错误")
+ res = int(h) * 3600 + int(m) * 60 + int(s)
+ if upperlimit is not None and res > upperlimit:
+ raise Exception("超过读取上限")
+ else:
+ return res
+ except Exception:
+ if error_count > 3:
+ logger.exception(f"读取失败{error_count}次超过上限")
+ return None
+ else:
+ logger.exception("读取失败")
+ return self.read_time(
+ cord, upperlimit, error_count + 1, use_digit_reader
+ )
diff --git a/arknights_mower/solvers/base_schedule.py b/arknights_mower/solvers/base_schedule.py
index 51a004d1d..054873070 100644
--- a/arknights_mower/solvers/base_schedule.py
+++ b/arknights_mower/solvers/base_schedule.py
@@ -1,1269 +1,2489 @@
-from __future__ import annotations
import copy
-import time
-from enum import Enum
-from datetime import datetime, timedelta
-import numpy as np
-import smtplib
-from email.mime.text import MIMEText
-from email.mime.multipart import MIMEMultipart
-
-from ..data import agent_list
-from ..utils import character_recognize, detector, segment
-from ..utils import typealias as tp
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
-from ..utils.datetime import get_server_weekday
-from paddleocr import PaddleOCR
-import cv2
-
-## Maa
-from arknights_mower.utils.asst import Asst, Message
import json
+import os
+import pathlib
+import sys
+import urllib
+from ctypes import CFUNCTYPE, c_char_p, c_int, c_void_p
+from datetime import datetime, timedelta
+from typing import Literal
-ocr = None
-
-class ArrangeOrder(Enum):
- STATUS = 1
- SKILL = 2
- FEELING = 3
- TRUST = 4
-
-
-arrange_order_res = {
- ArrangeOrder.STATUS: (1560 / 2496, 96 / 1404),
- ArrangeOrder.SKILL: (1720 / 2496, 96 / 1404),
- ArrangeOrder.FEELING: (1880 / 2496, 96 / 1404),
- ArrangeOrder.TRUST: (2050 / 2496, 96 / 1404),
-}
-
+import cv2
-class BaseSchedulerSolver(BaseSolver):
+from arknights_mower.data import agent_list, base_room_list
+from arknights_mower.solvers.base_mixin import BaseMixin
+from arknights_mower.solvers.credit import CreditSolver
+from arknights_mower.solvers.cultivate_depot import cultivate as cultivateDepotSolver
+from arknights_mower.solvers.depotREC import depotREC as DepotSolver
+from arknights_mower.solvers.mail import MailSolver
+from arknights_mower.solvers.reclamation_algorithm import ReclamationAlgorithm
+from arknights_mower.solvers.recruit import RecruitSolver
+from arknights_mower.solvers.report import ReportSolver
+from arknights_mower.solvers.secret_front import SecretFront
+from arknights_mower.solvers.shop import CreditShop
+from arknights_mower.solvers.skland import SKLand
+from arknights_mower.utils import config, detector, rapidocr
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.csleep import MowerExit, csleep
+from arknights_mower.utils.datetime import format_time, get_server_weekday
+from arknights_mower.utils.device.device import Device
+from arknights_mower.utils.digit_reader import DigitReader
+from arknights_mower.utils.email import maa_template, send_message
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, loadres, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.operators import Operator, Operators
+from arknights_mower.utils.path import get_path
+from arknights_mower.utils.plan import PlanTriggerTiming
+from arknights_mower.utils.recognize import Recognizer, Scene
+from arknights_mower.utils.scheduler_task import (
+ SchedulerTask,
+ TaskTypes,
+ add_release_dorm,
+ check_dorm_ordering,
+ find_next_task,
+ scheduling,
+ try_add_release_dorm,
+)
+
+
+class BaseSchedulerSolver(SceneGraphSolver, BaseMixin):
"""
收集基建的产物:物资、赤金、信赖
"""
def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
super().__init__(device, recog)
+ self.op_data = None
+ self.party_time = None
+ self.drone_time = None
+ self.reload_time = None
+ self.reload_room = None
+ self.clue_count_limit = 9
+ self.enable_party = True
+ self.leifeng_mode = False
+ self.digit_reader = DigitReader()
+ self.error = False
+ self.clue_count = 0
+ self.tasks = []
+ self.free_clue = None
+ self.credit_fight = None
+ self.task_count = 0
+ self.refresh_connecting = False
+ self.recruit_time = None
+ self.last_clue = None
+ self.sleeping = False
+ self.operators = {}
+
+ self.last_execution = {
+ "maa": None,
+ "recruit": None,
+ }
+
+ self.sign_in = (datetime.now() - timedelta(days=1, hours=4)).date()
+ self.daily_report = (datetime.now() - timedelta(days=1, hours=4)).date()
+ self.daily_skland = (datetime.now() - timedelta(days=1, hours=4)).date()
+ self.daily_mail = (datetime.now() - timedelta(days=1, hours=8)).date()
+ self.daily_visit_friend = (datetime.now() - timedelta(days=1, hours=4)).date()
+
+ def find_next_task(
+ self,
+ compare_time: datetime | None = None,
+ task_type="",
+ compare_type: Literal["<", "=", ">"] = "<",
+ meta_data="",
+ ):
+ """找符合条件的下一个任务
+
+ Args:
+ tasks: 任务列表
+ compare_time: 截止时间
+ """
+ return find_next_task(
+ self.tasks, compare_time, task_type, compare_type, meta_data
+ )
+
+ @property
+ def party_time(self):
+ return self._party_time
+
+ @party_time.setter
+ def party_time(self, value):
+ self._party_time = value
+ if self.op_data is not None:
+ self.op_data.party_time = value
def run(self) -> None:
"""
:param clue_collect: bool, 是否收取线索
"""
- self.currentPlan = self.global_plan[self.global_plan["default"]]
+
self.error = False
self.handle_error(True)
+
+ scheduling(self.tasks)
+ check_dorm_ordering(self.tasks, self.op_data)
if len(self.tasks) > 0:
# 找到时间最近的一次单个任务
self.task = self.tasks[0]
else:
self.task = None
+ if self.task is not None and datetime.now() < self.task.time:
+ reschedule_time = (self.task.time - datetime.now()).total_seconds()
+ if reschedule_time > 0:
+ logger.info(
+ f"出现任务调度情况休息{reschedule_time}秒等待下一个任务开始"
+ )
+ self.sleep(reschedule_time)
+ if self.party_time is not None and self.party_time < datetime.now():
+ self.party_time = None
+ if self.free_clue is not None and self.free_clue != get_server_weekday():
+ self.free_clue = None
+ if self.credit_fight is not None and self.credit_fight != get_server_weekday():
+ self.credit_fight = None
+ logger.debug(self.credit_fight)
self.todo_task = False
+ self.collect_notification = False
self.planned = False
- if len(self.operators.keys()) == 0:
- self.get_agent()
- if len(self.scan_time.keys()) == 0:
- self.scan_time = {k: None for k, v in self.currentPlan.items()}
+ if self.op_data is None or self.op_data.operators is None:
+ self.initialize_operators()
+ self.op_data.correct_dorm()
+ self.backup_plan_solver(PlanTriggerTiming.BEGINNING)
return super().run()
- def get_group(self, rest_agent, agent, groupname, name):
- for element in agent:
- if element['group'] == groupname and name != element["agent"]:
- rest_agent.append(element)
- # 从大组里删除
- agent.remove(element)
- return
-
def transition(self) -> None:
- self.recog.update()
- if self.scene() == Scene.INDEX:
- self.tap_element('index_infrastructure')
- elif self.scene() == Scene.INFRA_MAIN:
+ if (scene := self.scene()) == Scene.INFRA_MAIN:
return self.infra_main()
- elif self.scene() == Scene.INFRA_TODOLIST:
+ elif scene == Scene.INFRA_TODOLIST:
return self.todo_list()
- elif self.scene() == Scene.INFRA_DETAILS:
- self.back()
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_infrastructure')
- elif self.scene() == Scene.INFRA_ARRANGE_ORDER:
- self.tap_element('arrange_blue_yes')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
- self.last_room = ''
- logger.info("重设上次房间为空")
+ elif scene == Scene.RIIC_OPERATOR_SELECT:
+ self.tap_element("confirm_blue")
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown scene')
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
+ self.last_room = ""
+ logger.info("重设上次房间为空")
def overtake_room(self):
- name = self.task['type']
- candidate = []
- if self.operators[name]['group'] != '':
- candidate.extend([v['name'] for k, v in self.operators.items() if
- 'group' in v.keys() and v['group'] == self.operators[name]['group']])
- else:
- candidate.append(name)
- operators = []
- for i in candidate:
- _room = self.operators[name]['current_room']
- idx = [a['agent'] for a in self.current_base[_room]].index(i)
- operators.append({'agent': i, 'current_room': _room, 'room_index': idx})
- # if 'resting_priority' in self.operators[i].keys() and self.operators[i]['resting_priority']=='high':
- # room_need+=1
- resting_dorm = []
- ignore = []
- if next((e for e in self.tasks if 'type' in e.keys() and e['type'].startswith("dorm")), None) is not None:
- # 出去需要休息满的人其他清空
- for task in [e for e in self.tasks if 'type' in e.keys() and e['type'].startswith("dorm")]:
- for k, v in task['plan'].items():
- for a in v:
- if a == "Current":
- continue
- elif 'exhaust_require' in self.operators[a].keys() and self.operators[a]['exhaust_require']:
- ignore.append(a)
- resting_dorm.extend([self.operators[a]['current_room'] for a in ignore])
- # 执行全部任务
- for task in self.tasks:
- if 'type' in task.keys() and 'dorm' in task['type'] and 'plan' in task.keys():
- # TODO 移除 resting_room 的干员比如说有巫恋在休息
- self.agent_arrange(task['plan'])
- self.tasks.remove(task)
- self.get_swap_plan(resting_dorm, operators, False)
- def handle_error(self,force = False):
- # 如果有任何报错,则生成一个空
+ candidates = self.task.meta_data.split(",")
+ if len(candidates) == 0:
+ return
+ if self.op_data.operators[candidates[0]].group != "":
+ candidates = self.op_data.groups[
+ self.op_data.operators[candidates[0]].group
+ ]
+ logger.debug(f"更新下班小组信息为{candidates}")
+ # 在candidate 中,计算出需要的high free 和 Low free 数量
+ _high_free = 0
+ _low_free = 0
+ remove_name = []
+ for x in candidates:
+ if (
+ self.op_data.operators[x].is_resting()
+ or self.op_data.operators[x].current_mood() == 24
+ ):
+ remove_name.append(x)
+ else:
+ if self.op_data.operators[x].resting_priority == "high":
+ _high_free += 1
+ else:
+ _low_free += 1
+ candidates = [x for x in candidates if x not in remove_name]
+ self.agent_get_mood(force=True)
+ # 剩余高效组位置
+ current_high = self.op_data.available_free()
+ # 剩余低效位置
+ current_low = self.op_data.available_free("low")
+ logger.debug(f"剩余高效:{current_high},低效:{current_low}")
+ logger.debug(f"需求高效:{_high_free},低效:{_low_free}")
+ success = False
+ if current_high >= _high_free and current_low >= _low_free:
+ # 检查是否目前宿舍满足 low free 和high free 的数量需求,如果满足,则直接安排
+ _plan = {}
+ _replacement = []
+ _replacement, _plan, current_high, current_low = self.get_resting_plan(
+ candidates, _replacement, _plan, current_high, current_low
+ )
+ if len(_plan.items()) > 0:
+ self.tasks.append(
+ SchedulerTask(
+ datetime.now(), task_plan=_plan, task_type=TaskTypes.SHIFT_OFF
+ )
+ )
+ success = True
+ else:
+ msg = f"无法完成 {self.task.meta_data} 的排班,如果重复接收此邮件请检查替换组是否被占用"
+ send_message(msg, level="ERROR")
+ if not success:
+ # 如果不满足,则找到并且执行最近一个type 包含 超过数量的high free 和low free 的 任务并且 干员没有 exaust_require 词条
+ task_index = -1
+ current_high, current_low = 0, 0
+ for idx, task in enumerate(self.tasks):
+ if "dorm" in task.meta_data:
+ # 检查数量
+ ids = [int(w[4:]) for w in task.meta_data.split(",")]
+ is_exhaust_require = False
+ for _id in ids:
+ if not is_exhaust_require:
+ if (
+ self.op_data.dorm[_id].name
+ in self.op_data.exhaust_agent
+ ):
+ is_exhaust_require = True
+
+ if _id > self.op_data.config.max_resting_count - 1:
+ current_low += 1
+ else:
+ current_high += 1
+ # 休息满需要则跳过
+ if (
+ current_high >= _high_free
+ and current_low >= _low_free
+ and not is_exhaust_require
+ ):
+ task_index = idx
+ else:
+ current_low, current_high = 0, 0
+ if task_index > -1:
+ # 修改执行时间
+ self.tasks[task_index].time = datetime.now()
+ # 执行完提前换班任务再次执行本任务
+ self.tasks.append(
+ SchedulerTask(
+ task_plan=copy.deepcopy(self.task.plan),
+ meta_data=self.task.meta_data,
+ task_type=self.task.type,
+ )
+ )
+ else:
+ # 任务全清
+ rooms = []
+ remove_idx = []
+ for idx, task in enumerate(self.tasks):
+ if "dorm" in task.meta_data:
+ # 检查数量
+ ids = [int(w[4:]) for w in task.meta_data.split(",")]
+ is_exhaust_require = False
+ _rooms = []
+ for _id in ids:
+ if not is_exhaust_require:
+ if (
+ self.op_data.dorm[_id].name
+ in self.op_data.exhaust_agent
+ ):
+ is_exhaust_require = True
+ __room = self.op_data.operators[
+ self.op_data.dorm[_id].name
+ ].room
+ if __room not in _rooms:
+ _rooms.append(__room)
+ # 跳过需要休息满
+ if not is_exhaust_require:
+ rooms.extend(_rooms)
+ remove_idx.append(idx)
+ for idx in sorted(remove_idx, reverse=True):
+ self.tasks.pop(idx)
+ plan = {}
+ for room in rooms:
+ if room not in plan.keys():
+ plan[room] = [data.agent for data in self.op_data.plan[room]]
+ if len(plan.keys()) > 0:
+ self.tasks.append(SchedulerTask(task_plan=plan))
+ # 执行完提前换班任务再次执行本任务
+ self.tasks.append(
+ SchedulerTask(
+ task_plan=copy.deepcopy(self.task.plan),
+ meta_data=self.task.meta_data,
+ task_type=self.task.type,
+ )
+ )
+ self.skip()
+ return
+
+ def handle_error(self, force=False):
if self.scene() == Scene.UNKNOWN:
- self.device.exit('com.hypergryph.arknights')
+ self.device.exit()
+ self.check_current_focus()
if self.error or force:
# 如果没有任何时间小于当前时间的任务才生成空任务
- if (next((e for e in self.tasks if e['time'] < datetime.now()), None)) is None:
- room = next(iter(self.currentPlan.keys()))
- logger.info("由于出现错误情况,生成一次空任务来执行纠错")
- self.tasks.append({'time': datetime.now(), 'plan': {room: ['Current'] * len(self.currentPlan[room])}})
+ if self.find_next_task(datetime.now()) is None:
+ logger.debug("由于出现错误情况,生成一次空任务来执行纠错")
+ self.tasks.append(SchedulerTask())
# 如果没有任何时间小于当前时间的任务-10分钟 则清空任务
- if (next((e for e in self.tasks if e['time'] < datetime.now() - timedelta(seconds=600)), None)) is not None:
- logger.info("检测到执行超过10分钟的任务,清空全部任务")
+ if self.find_next_task(datetime.now() - timedelta(seconds=900)):
+ logger.info("检测到执行超过15分钟的任务,清空全部任务")
self.tasks = []
- self.scan_time = {}
- self.operators = {}
+ elif self.find_next_task(datetime.now() + timedelta(hours=2.5)) is None:
+ logger.debug("2.5小时内没有其他任务,生成一个空任务")
+ self.tasks.append(SchedulerTask(time=datetime.now() + timedelta(hours=2.5)))
return True
- def plan_metadata(self, time_result):
- group_info = self.task['metadata']['plan']
- read_time_rooms = self.task['metadata']['room']
- if time_result is None:
- time_result = {}
- for key, _plan in group_info.items():
- if 'default' != key:
- _plan["time"] = time_result[_plan["type"]]
- self.tasks.append({"type": _plan['type'], 'plan': _plan['plan'], 'time': _plan['time']})
- else:
- # 合在一起则取最小恢复时间
- min_time = datetime.max
- __time = datetime.now()
- if len(self.task['metadata']['room']) == 0:
- # 如果没有读取任何时间,则只休息1小时替换下一组
- _plan["time"] = __time + timedelta(seconds=(3600))
+ def plan_fia(self):
+ fia_plan, fia_room = self.check_fia()
+ if fia_room is None or fia_plan is None:
+ return
+ # 肥鸭充能新模式:https://github.com/ArkMowers/arknights-mower/issues/551
+ target = None
+ fia_threshold = 0.9
+ for operator in fia_plan:
+ data = self.op_data.operators[operator]
+ operator_morale = data.current_mood()
+ operator_limit = data.lower_limit
+ logger.debug(f"{operator}的心情为{operator_morale}")
+ if operator_morale > fia_threshold * 24:
+ logger.debug(f"{operator}的心情高于阈值,跳过充能")
+ continue
+ if data.rest_in_full and data.exhaust_require and not data.is_resting():
+ logger.debug(f"{operator}为暖机干员但不在宿舍,跳过充能")
+ continue
+ if data.group:
+ lowest = True
+ for member in self.op_data.groups[data.group]:
+ if member == operator:
+ continue
+ # Lancet-2
+ if (
+ self.op_data.operators[member].workaholic
+ and member not in fia_plan
+ ):
+ continue
+ member_morale = self.op_data.operators[member].current_mood()
+ member_limit = self.op_data.operators[member].lower_limit
+ logger.debug(f"{data.group}组内{member}的心情为{member_morale}")
+ if member_morale - member_limit < operator_morale - operator_limit:
+ lowest = False
+ logger.debug(f"{operator}的心情高于{member},跳过充能")
+ break
+ if not lowest:
+ continue
+ target = operator
+ break
+ if target:
+ self.tasks.append(
+ SchedulerTask(
+ time=self.task.time,
+ task_plan={fia_room: [target, "菲亚梅塔"]},
+ )
+ )
+ # 充能结束后整组立即上班
+ for task in self.tasks:
+ if task.type == TaskTypes.SHIFT_ON:
+ for room, operators in task.plan.items():
+ if target in operators:
+ task.time = self.task.time + timedelta(seconds=1)
+ self.tasks.sort(key=lambda task: task.time)
+ return
+ else:
+ logger.info("肥鸭充能干员不足,请添加更多干员!")
+ self.tasks.append(
+ SchedulerTask(
+ time=self.task.time + timedelta(hours=24 * (1 - fia_threshold) / 2),
+ task_type=TaskTypes.FIAMMETTA,
+ )
+ )
+ self.tasks.sort(key=lambda task: task.time)
+
+ def plan_metadata(self):
+ planned_index = []
+ for t in self.tasks:
+ if "dorm" in t.meta_data:
+ planned_index.extend([int(w[4:]) for w in t.meta_data.split(",")])
+ _time = datetime.max
+ _plan = {}
+ _type = []
+ # 第一个心情低的且小于3 则只休息半小时
+ short_rest = False
+ self.total_agent = list(
+ v
+ for k, v in self.op_data.operators.items()
+ if v.is_high() and not v.room.startswith("dorm") and not v.is_resting()
+ )
+ self.total_agent.sort(
+ key=lambda x: x.current_mood() - x.lower_limit, reverse=False
+ )
+ if (
+ next(
+ (
+ a
+ for a in self.total_agent
+ if (a.name not in self.op_data.exhaust_agent)
+ and not a.workaholic
+ and a.current_mood() <= 3
+ ),
+ None,
+ )
+ is not None
+ ):
+ short_rest = True
+ low_priority = []
+ for idx, dorm in enumerate(self.op_data.dorm):
+ logger.debug(f"开始计算{dorm}")
+ # 如果已经plan了,则跳过
+ if idx in planned_index or idx in low_priority:
+ continue
+ _name = dorm.name
+ if _name == "":
+ continue
+ # 如果是rest in full,则新增单独任务..
+ if (
+ _name in self.op_data.operators.keys()
+ and self.op_data.operators[_name].rest_in_full
+ ):
+ __plan = {}
+ __rest_agent = []
+ __type = []
+ if self.op_data.operators[dorm.name].group == "":
+ __rest_agent.append(dorm.name)
else:
- for dorm in _plan["type"].split(','):
- if dorm not in read_time_rooms:
- continue
- if dorm in time_result.keys() and min_time > time_result[dorm]:
- min_time = time_result[dorm]
- _plan["time"] = min_time
- # 如果有任何已有plan
- existing_plan = next(
- (e for e in self.tasks if 'type' in e.keys() and e['type'].startswith('dormitory')), None)
- if existing_plan is not None and existing_plan['time'] < _plan["time"]:
- for k in _plan['plan']:
- if k not in existing_plan['plan']:
- existing_plan['plan'][k] = _plan['plan'][k]
- else:
- for idx, _a in enumerate(_plan['plan'][k]):
- if _plan['plan'][k][idx] != 'Current':
- existing_plan['plan'][k][idx] = _plan['plan'][k][idx]
- existing_plan['type'] = existing_plan['type'] + ',' + _plan["type"]
+ __rest_agent.extend(
+ self.op_data.groups[self.op_data.operators[dorm.name].group]
+ )
+ if dorm.time is not None:
+ __time = dorm.time
+ else:
+ __time = datetime.max
+ for x in __rest_agent:
+ # 如果同小组也是rest_in_full则取最大休息时间 否则忽略
+ if x in low_priority:
+ logger.debug("检测到回满组已经安排")
+ _plan = {}
+ _idx, __dorm = self.op_data.get_dorm_by_name(x)
+ if (
+ x in self.op_data.operators.keys()
+ and self.op_data.operators[x].rest_in_full
+ ):
+ if __dorm is not None and __dorm.time is not None:
+ if (
+ __dorm.time > __time
+ and self.op_data.operators[x].resting_priority == "high"
+ ):
+ __time = __dorm.time
+ if _idx is not None:
+ __type.append("dorm" + str(_idx))
+ planned_index.append(_idx)
+ __room = self.op_data.operators[x].room
+ if __room not in __plan.keys():
+ __plan[__room] = ["Current"] * len(self.op_data.plan[__room])
+ __plan[__room][self.op_data.operators[x].index] = x
+ if __time < datetime.now():
+ __time = datetime.now()
+ if __time != datetime.max:
+ self.tasks.append(
+ SchedulerTask(
+ time=__time,
+ task_type=TaskTypes.SHIFT_ON,
+ task_plan=__plan,
+ meta_data=",".join(__type),
+ )
+ )
+ try_add_release_dorm(__plan, __time, self.op_data, self.tasks)
+ else:
+ self.op_data.reset_dorm_time()
+ self.error = True
+ # 如果非 rest in full, 则同组取时间最小值
+ elif (
+ _name in self.op_data.operators.keys()
+ and not self.op_data.operators[_name].is_high()
+ and self.op_data.config.free_room
+ ):
+ # 释放满心情其他干员
+ add_release_dorm(self.tasks, self.op_data, _name)
+ elif self.op_data.operators[_name].is_high():
+ if dorm.time is not None and dorm.time < _time:
+ logger.debug(f"更新任务时间{dorm.time}")
+ _time = dorm.time
+ __room = self.op_data.operators[_name].room
+ __rest_agent = []
+ if self.op_data.operators[_name].group == "":
+ __rest_agent.append(_name)
else:
- self.tasks.append({"type": _plan['type'], 'plan': _plan['plan'], 'time': _plan['time']})
+ __rest_agent.extend(
+ self.op_data.groups[self.op_data.operators[_name].group]
+ )
+ logger.debug(f"小组分组为{__rest_agent}")
+ # 如果小组有其他人是rest_in_full则跳过
+ if next(
+ (
+ d
+ for d in __rest_agent
+ if d in self.op_data.operators.keys()
+ and self.op_data.operators[d].rest_in_full
+ ),
+ None,
+ ):
+ continue
+ for x in __rest_agent:
+ if x in low_priority:
+ continue
+ __room = self.op_data.operators[x].room
+ if __room not in base_room_list:
+ continue
+ if __room not in _plan.keys():
+ _plan[__room] = ["Current"] * len(self.op_data.plan[__room])
+ _plan[__room][self.op_data.operators[x].index] = x
+ _dorm_idx, __dorm = self.op_data.get_dorm_by_name(x)
+ if __dorm is not None:
+ _type.append("dorm" + str(_dorm_idx))
+ planned_index.append(_dorm_idx)
+ if (
+ __dorm.time is not None
+ and __dorm.time < _time
+ and self.op_data.operators[x].resting_priority == "high"
+ ):
+ logger.debug(f"更新任务时间{dorm.time}")
+ _time = __dorm.time
+
+ if x not in low_priority:
+ low_priority.append(x)
+ # 生成单个任务
+ if len(_plan.items()) > 0:
+ if _time != datetime.max:
+ _time -= timedelta(minutes=8)
+ if _time < datetime.now():
+ _time = datetime.now()
+ _time = (
+ _time if not short_rest else (datetime.now() + timedelta(hours=0.5))
+ )
+ self.tasks.append(
+ SchedulerTask(
+ time=_time,
+ task_plan=_plan,
+ task_type=TaskTypes.SHIFT_ON,
+ meta_data=",".join(_type),
+ )
+ )
+ try_add_release_dorm(_plan, _time, self.op_data, self.tasks)
+ else:
+ logger.debug("检测到时间数据不存在")
+ self.op_data.reset_dorm_time()
+ self.error = True
+ # 最后再做不养闲人刷新
+ if self.op_data.config.free_room:
+
+ def should_keep(task):
+ if task.type != TaskTypes.RELEASE_DORM:
+ return True
+ i, d = self.op_data.get_dorm_by_name(task.meta_data)
+ if i is None:
+ logger.info(f"检测到{task.meta_data}不在宿舍,移除相关任务")
+ return False
+ return True
+
+ self.tasks = [t for t in self.tasks if should_keep(t)]
def infra_main(self):
- """ 位于基建首页 """
- if self.find('control_central') is None:
+ """位于基建首页"""
+ if self.find("control_central") is None:
self.back()
return
if self.task is not None:
try:
- if len(self.task["plan"].keys()) > 0:
- read_time_room = []
- if 'metadata' in self.task.keys():
- read_time_room = self.task['metadata']['room']
- time_result = self.agent_arrange(self.task["plan"], read_time_room)
- if 'metadata' in self.task.keys():
- self.plan_metadata(time_result)
- else:
+ if len(self.task.plan.keys()) > 0:
+ get_time = False
+ if TaskTypes.SHIFT_OFF == self.task.type:
+ get_time = True
+ if TaskTypes.RELEASE_DORM == self.task.type:
+ # 如果该房间提前已经被移出,则跳过安排避免影响正常排班
+ free_room = list(self.task.plan.keys())[0]
+ free_index = self.task.plan[free_room].index("Free")
+ if self.task.meta_data in self.op_data.operators.keys():
+ free_agent = self.op_data.operators[self.task.meta_data]
+ if (
+ free_agent.current_room == free_room
+ and free_agent.current_index == free_index
+ ):
+ get_time = True
+ # 如果是高优先,还需要把宿舍reference移除
+ if free_agent.is_high():
+ idx, dorm = self.op_data.get_dorm_by_name(
+ free_agent.name
+ )
+ if idx is not None:
+ update_task = find_next_task(
+ self.tasks,
+ task_type=TaskTypes.SHIFT_ON,
+ meta_data="dorm" + str(idx),
+ )
+ if update_task:
+ logger.debug("开始更新宿舍信息")
+ dorm_list = update_task.meta_data.split(",")
+ dorm_list.remove("dorm" + str(idx))
+ update_task.meta_data = ",".join(dorm_list)
+ free_agent.mood = free_agent.upper_limit
+ free_agent.time_stamp = dorm.time
+ else:
+ self.task.plan = {}
+ else:
+ self.task.plan = {}
+ if (
+ config.conf.run_order_grandet_mode.back_to_index
+ and TaskTypes.RUN_ORDER == self.task.type
+ and not self.refresh_connecting
+ and config.conf.run_order_buffer_time > 0
+ ):
+ logger.info("跑单前返回主界面以保持登录状态")
+ self.back_to_index()
+ self.refresh_connecting = True
+ return
+ self.refresh_connecting = False
+ self.agent_arrange(self.task.plan, get_time)
+ if get_time:
+ self.backup_plan_solver(PlanTriggerTiming.BEFORE_PLANNING)
+ if (
+ self.find_next_task(datetime.now() + timedelta(seconds=15))
+ is None
+ ):
+ self.plan_metadata()
+ else:
+ logger.info("检测到15秒内有额外任务,跳过plan")
+ if TaskTypes.RE_ORDER == self.task.type:
+ self.skip()
+ # 如果任务名称包含干员名,则为动态生成的
+ elif self.task.type == TaskTypes.FIAMMETTA:
+ self.plan_fia()
+ elif (
+ self.task.meta_data.split(",")[0] in agent_list
+ and self.task.type == TaskTypes.EXHAUST_OFF
+ ):
self.overtake_room()
+ elif self.task.type == TaskTypes.CLUE_PARTY:
+ self.party_time = None
+ self.last_clue = None
+ self.clue_new()
+ self.last_clue = datetime.now()
+ self.skip(["planned", "collect_notification"])
+ elif self.task.type == TaskTypes.REFRESH_TIME:
+ if self.task.meta_data == "train":
+ if upgrade := self.find_next_task(
+ task_type=TaskTypes.SKILL_UPGRADE
+ ):
+ self.refresh_skill_time(upgrade)
+ else:
+ self.plan_run_order(self.task.meta_data)
+ self.skip(["planned", "todo_task", "collect_notification"])
+ elif self.task.type == TaskTypes.SKILL_UPGRADE:
+ self.skill_upgrade(self.task.meta_data)
del self.tasks[0]
+ if self.tasks and self.tasks[0].type in [TaskTypes.SHIFT_ON]:
+ self.backup_plan_solver(PlanTriggerTiming.AFTER_PLANNING)
+ except MowerExit:
+ raise
except Exception as e:
logger.exception(e)
- self.planned = True
- self.todo_task = True
- self.error = True
+ if (
+ type(e) is ConnectionAbortedError
+ or type(e) is AttributeError
+ or type(e) is ConnectionError
+ ):
+ raise e
+ else:
+ self.skip()
+ self.error = True
self.task = None
- elif not self.todo_task:
- # 处理基建 Todo
- notification = detector.infra_notification(self.recog.img)
- if notification is None:
- self.sleep(1)
- notification = detector.infra_notification(self.recog.img)
- if notification is not None:
- self.tap(notification)
- else:
- self.todo_task = True
elif not self.planned:
try:
# 如果有任何type 则会最后修正
- if self.read_mood:
- mood_result = self.agent_get_mood(True)
+ if not self.no_pending_task(2):
+ self.skip(["planned", "todo_task", "collect_notification"])
+ else:
+ mood_result = self.agent_get_mood(skip_dorm=True)
if mood_result is not None:
+ self.skip(["planned", "todo_task", "collect_notification"])
return True
- self.plan_solver()
+ self.plan_solver()
+ except MowerExit:
+ raise
except Exception as e:
- # 重新扫描
- self.error = True
- logger.exception({e})
+ logger.exception(e)
+ if (
+ type(e) is ConnectionAbortedError
+ or type(e) is AttributeError
+ or type(e) is ConnectionError
+ ):
+ raise e
+ else:
+ self.error = True
self.planned = True
+ elif not self.todo_task:
+ if (
+ self.enable_party
+ and (
+ self.last_clue is None
+ or datetime.now() - self.last_clue > timedelta(hours=1)
+ )
+ and self.no_pending_task(3)
+ ):
+ self.clue_new()
+ self.last_clue = datetime.now()
+ # if (self.party_time is None or self.free_clue is None) and self.enable_party:
+ # self.clue()
+ # if self.clue_count > self.clue_count_limit and self.enable_party:
+ # self.share_clue()
+ if (
+ self.drone_room not in self.op_data.run_order_rooms
+ and (
+ self.drone_time is None
+ or self.drone_time
+ < datetime.now() - timedelta(hours=config.conf.drone_interval)
+ )
+ and self.drone_room is not None
+ and self.no_pending_task(2)
+ ):
+ self.drone(self.drone_room)
+ logger.info(f"记录本次无人机使用时间为:{datetime.now()}")
+ self.drone_time = datetime.now()
+ if (
+ self.reload_room is not None
+ and self.no_pending_task(2)
+ and (
+ self.reload_time is None
+ or self.reload_time
+ < datetime.now() - timedelta(hours=config.conf.maa_gap)
+ )
+ ):
+ self.reload()
+ logger.info(f"记录本次补货时间为:{datetime.now()}")
+ self.todo_task = True
+ elif not self.collect_notification:
+ if self.no_pending_task(1):
+ notification = detector.infra_notification(self.recog.img)
+ if notification is None:
+ self.sleep(1)
+ notification = detector.infra_notification(self.recog.img)
+ if notification is not None:
+ self.tap(notification)
+ self.collect_notification = True
else:
return self.handle_error()
- def get_plan(self, room, index=-1):
- # -1 就是不换人
- result = []
- for data in self.currentPlan[room]:
- result.append(data["agent"])
- return result
-
- def agent_get_mood(self,skip_dorm = False):
- # 如果5分钟之内有任务则跳过心情读取
- if next((k for k in self.tasks if k['time'] (
- datetime.now() - timedelta(seconds=5400)))))
+ def translate_room(self, room):
+ translations = {
+ "room": lambda parts: f"B{parts[1]}0{parts[2]}",
+ "dormitory": lambda parts: f"{parts[1]}层宿舍",
+ "contact": lambda parts: "办公室",
+ "central": lambda parts: "控制中枢",
+ "factory": lambda parts: "加工站",
+ "meeting": lambda parts: "会客室",
+ }
+
+ for keyword, translation_func in translations.items():
+ if keyword in room:
+ parts = room.split("_")
+ return translation_func(parts)
+
+ return room
+
+ def agent_get_mood(self, skip_dorm=False, force=False):
+ # 暂时规定纠错只适用于主班表
+ need_read = set(
+ v.room
+ for k, v in self.op_data.operators.items()
+ if v.need_to_refresh() and v.room in base_room_list
+ )
for room in need_read:
error_count = 0
+ # 由于训练室不纠错,如果训练室有干员且时间读取过就跳过
+ if room == "train":
+ first = next(
+ (
+ (value)
+ for key, value in self.op_data.operators.items()
+ if value.current_room == "train"
+ ),
+ None,
+ )
+ if first is not None and first.time_stamp > datetime.now() - timedelta(
+ hours=2.5
+ ):
+ continue
while True:
try:
self.enter_room(room)
- self.current_base[room] = self.get_agent_from_room(room)
- logger.info(f'房间 {room} 心情为:{self.current_base[room]}')
+ _mood_data = self.get_agent_from_room(room)
+ mood_info = [
+ f"干员: '{item['agent']}', 心情: {round(item['mood'], 3)}"
+ for item in _mood_data
+ ]
+ logger.info(f"房间 {self.translate_room(room)} {mood_info}")
+ # logger.info(f'房间 {room} 心情为:{_mood_data}')
break
+ except MowerExit:
+ raise
except Exception as e:
- if error_count > 3: raise e
- logger.error(e)
+ logger.exception(e)
+ if error_count > 3:
+ raise e
error_count += 1
self.back()
continue
self.back()
- if '' in self.operators.keys(): self.operators['']['current_room'] = ''
- logger.debug(self.operators)
- for room in self.currentPlan.keys():
- for idx, item in enumerate(self.currentPlan[room]):
- _name = next((k for k, v in self.operators.items() if
- v['current_room'] == room and 'current_index' in v.keys() and v['current_index'] == idx),
- None)
- if room not in self.current_base.keys():
- self.current_base[room] = [''] * len(self.currentPlan[room])
- if _name is None or _name == '':
- self.current_base[room][idx] = {"agent": "", "mood": -1}
- else:
- self.current_base[room][idx] = {"mood": self.operators[_name]['mood'], "agent": _name}
- current_base = copy.deepcopy(self.current_base)
- plan = self.currentPlan
- self.total_agent = []
+ plan = self.op_data.plan
fix_plan = {}
- for key in current_base:
- if (key == 'train'): continue
+ for key in plan:
+ if key == "train":
+ continue
need_fix = False
- for idx, operator in enumerate(current_base[key]):
- data = current_base[key][idx]
+ _current_room = self.op_data.get_current_room(key, True)
+ for idx, name in enumerate(_current_room):
# 如果是空房间
- if data["agent"] == '':
+ if name == "":
if not need_fix:
- fix_plan[key] = [''] * len(plan[key])
+ fix_plan[key] = ["Current"] * len(plan[key])
need_fix = True
- fix_plan[key][idx] = plan[key][idx]["agent"]
+ fix_plan[key][idx] = plan[key][idx].agent
continue
- if (data["agent"] in self.agent_base_config.keys()):
- # 如果有设置下限,则减去下限值 eg: 令
- if ("LowerLimit" in self.agent_base_config[current_base[key][idx]["agent"]]):
- data["mood"] = data["mood"] - self.agent_base_config[current_base[key][idx]["agent"]]["LowerLimit"]
- # 把额外数据传过去
- data["current_room"] = key
- data["room_index"] = idx
- # 记录数据
- self.total_agent.append(data)
# 随意人员则跳过
- if plan[key][idx]["agent"] == 'Free':
+ if plan[key][idx].agent == "Free":
continue
- if not (data['agent'] == plan[key][idx]['agent'] or (
- (data["agent"] in plan[key][idx]["replacement"]) and len(plan[key][idx]["replacement"]) > 0)):
+ if not (
+ name == plan[key][idx].agent
+ or (
+ (
+ name in plan[key][idx].replacement
+ and name not in ["但书", "龙舌兰", "佩佩"]
+ )
+ and len(plan[key][idx].replacement) > 0
+ )
+ ):
if not need_fix:
- fix_plan[key] = [''] * len(plan[key])
+ fix_plan[key] = ["Current"] * len(plan[key])
need_fix = True
- fix_plan[key][idx] = plan[key][idx]["agent"]
- elif need_fix:
- fix_plan[key][idx] = data["agent"]
- # 检查是否有空名
- if need_fix:
- for idx, fix_agent in enumerate(fix_plan[key]):
- if fix_plan[key][idx] == '':
- # 则使用当前干员
- fix_plan[key][idx] = 'Current'
+ fix_plan[key][idx] = plan[key][idx].agent
# 最后如果有任何高效组心情没有记录 或者高效组在宿舍
- miss_list = {k: v for (k, v) in self.operators.items() if
- (v['type'] == 'high' and ('mood' not in v.keys() or (v['mood'] == -1 or (v['mood'] == 24) and v['current_room'].startswith('dormitory'))))}
+ miss_list = {k: v for (k, v) in self.op_data.operators.items() if v.not_valid()}
if len(miss_list.keys()) > 0:
# 替换到他应该的位置
+ logger.debug(f"高效组心情没有记录{str(miss_list)}")
for key in miss_list:
- if miss_list[key]['group'] != '':
- # 如果还有其他小组成员没满心情则忽略
- if next((k for k, v in self.operators.items() if
- v['group'] == miss_list[key]['group'] and v['current_room'].startswith(
- 'dormitory') and not (v['mood'] == -1 or v['mood'] == 24)), None) is not None:
- continue
- if miss_list[key]['room'] not in fix_plan.keys():
- fix_plan[miss_list[key]['room']] = [x['agent'] for x in current_base[miss_list[key]['room']]]
- fix_plan[miss_list[key]['room']][miss_list[key]['index']] = key
+ _agent = miss_list[key]
+ if (
+ _agent.group != ""
+ and next(
+ (
+ k
+ for k, v in self.op_data.operators.items()
+ if v.group == _agent.group
+ and not v.not_valid()
+ and v.is_resting()
+ ),
+ None,
+ )
+ is not None
+ and (
+ _agent.current_mood() == _agent.upper_limit
+ or _agent.workaholic
+ or _agent.mood == _agent.upper_limit
+ )
+ ):
+ continue
+ elif _agent.group != "":
+ # 把所有小组成员都移到工作站
+ agents = self.op_data.groups[_agent.group]
+ for a in agents:
+ __agent = self.op_data.operators[a]
+ if __agent.room not in fix_plan.keys():
+ fix_plan[__agent.room] = ["Current"] * len(
+ self.op_data.plan[__agent.room]
+ )
+ fix_plan[__agent.room][__agent.index] = a
+ if _agent.room not in fix_plan.keys():
+ fix_plan[_agent.room] = ["Current"] * len(
+ self.op_data.plan[_agent.room]
+ )
+ fix_plan[_agent.room][_agent.index] = key
+ # 如果是错位:
+ if (
+ _agent.current_index != -1 and _agent.current_index != _agent.index
+ ) or (_agent.current_room != "" and _agent.room != _agent.current_room):
+ moved_room = _agent.current_room
+ moved_index = _agent.current_index
+ if moved_room not in fix_plan.keys():
+ fix_plan[moved_room] = ["Current"] * len(
+ self.op_data.plan[moved_room]
+ )
+ fix_plan[moved_room][moved_index] = self.op_data.plan[moved_room][
+ moved_index
+ ].agent
if len(fix_plan.keys()) > 0:
# 不能在房间里安排同一个人 如果有重复则换成Free
- # 还要修复确保同一组在同时上班
- fix_agents = []
remove_keys = []
+ logger.debug(f"Fix_plan {str(fix_plan)}")
for key in fix_plan:
- if skip_dorm and 'dormitory' in key:
- remove_keys.append(key)
- for idx, fix_agent in enumerate(fix_plan[key]):
- if fix_agent not in fix_agents:
- fix_agents.append(fix_agent)
- else:
- fix_plan[key][idx] = "Free" if fix_plan[key][idx] not in ['Free', 'Current'] else \
- fix_plan[key][idx]
+ if "dormitory" in key:
+ # 如果宿舍差Free干员 则跳过
+ if (
+ next(
+ (e for e in fix_plan[key] if e not in ["Free", "Current"]),
+ None,
+ )
+ is None
+ and skip_dorm
+ ):
+ remove_keys.append(key)
+ continue
if len(remove_keys) > 0:
for item in remove_keys:
del fix_plan[item]
+ # 还要确保同一组在同时上班
+ for g in self.op_data.groups:
+ g_agents = self.op_data.groups[g]
+ is_any_working = next(
+ (
+ x
+ for x in g_agents
+ if self.op_data.operators[x].current_room != ""
+ and not self.op_data.operators[x].is_resting()
+ ),
+ None,
+ )
+ if is_any_working is not None:
+ # 确保所有人同时在上班
+ is_any_resting = next(
+ (
+ x
+ for x in g_agents
+ if self.op_data.operators[x].current_room == ""
+ or self.op_data.operators[x].is_resting()
+ ),
+ None,
+ )
+ if is_any_resting is not None:
+ # 生成纠错任务
+ for x in g_agents:
+ if (
+ self.op_data.operators[x].current_room == ""
+ or self.op_data.operators[x].is_resting()
+ ):
+ room = self.op_data.operators[x].room
+ if room not in fix_plan:
+ fix_plan[room] = ["Current"] * len(plan[room])
+ fix_plan[room][self.op_data.operators[x].index] = x
if len(fix_plan.keys()) > 0:
- self.tasks.append({"plan": fix_plan, "time": datetime.now()})
- logger.info(f'纠错任务为-->{fix_plan}')
- return "self_correction"
+ # 如果5分钟之内有任务则跳过心情读取
+ next_task = self.find_next_task()
+ second = (
+ 0
+ if next_task is None
+ else (next_task.time - datetime.now()).total_seconds()
+ )
+ # 如果下个任务的操作时间超过下个任务,则跳过
+ if (
+ not force
+ and next_task is not None
+ and len(fix_plan.keys()) * 45 > second
+ ):
+ logger.info("有未完成的任务,跳过纠错")
+ self.skip()
+ return
+ else:
+ self.tasks.append(
+ SchedulerTask(
+ task_plan=fix_plan, task_type=TaskTypes.SELF_CORRECTION
+ )
+ )
+ logger.info(f"纠错任务为-->{fix_plan}")
+ return "self_correction"
+
+ def refresh_skill_time(self, task):
+ try:
+ unknown_cnt = 0
+ tasks = ["refresh"]
+ while tasks:
+ scene = self.train_scene()
+ if scene == Scene.UNKNOWN:
+ unknown_cnt += 1
+ if unknown_cnt > 5:
+ unknown_cnt = 0
+ self.back_to_infrastructure()
+ self.enter_room("train")
+ else:
+ self.sleep()
+ continue
+ if scene == Scene.CONNECTING:
+ self.sleep(1)
+ if scene == Scene.INFRA_MAIN:
+ self.enter_room("train")
+ if scene == Scene.TRAIN_MAIN:
+ task.time = self.double_read_time(
+ (
+ (236, 978),
+ (380, 1020),
+ ),
+ use_digit_reader=True,
+ )
+ del tasks[0]
+ if scene == Scene.TRAIN_SKILL_SELECT:
+ self.back()
+ if scene == Scene.TRAIN_SKILL_UPGRADE:
+ self.back()
+ self.back()
+ except Exception as e:
+ logger.exception(e)
- def plan_solver(self):
- plan = self.currentPlan
- # 如果下个 普通任务 <10 分钟则跳过 plan
- if (
- next((e for e in self.tasks if 'type' not in e.keys() and e['time'] < datetime.now() + timedelta(seconds=600)),
- None)) is not None:
+ def skill_upgrade(self, skill):
+ try:
+ unknown_cnt = 0
+ tasks = ["collect", "upgrade", "confirm"]
+ execute_time = None
+ level = 1
+ while tasks:
+ scene = self.train_scene()
+ if scene == Scene.UNKNOWN:
+ unknown_cnt += 1
+ if unknown_cnt > 5:
+ unknown_cnt = 0
+ self.back_to_infrastructure()
+ self.enter_room("train")
+ else:
+ self.sleep()
+ elif scene == Scene.CONNECTING:
+ self.sleep()
+ elif scene == Scene.INFRA_MAIN:
+ self.enter_room("train")
+ elif scene == Scene.TRAIN_FINISH:
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.5)
+ elif scene == Scene.TRAIN_MAIN:
+ if tasks[0] == "collect":
+ completed = self.find("training_completed")
+ if completed:
+ logger.debug("训练完成")
+ self.tap(completed, interval=3)
+ del tasks[0]
+ else:
+ logger.info("检测到专精未完成,刷新任务时间")
+ execute_time = self.double_read_time(
+ (
+ (236, 978),
+ (380, 1020),
+ ),
+ use_digit_reader=True,
+ )
+ if execute_time > datetime.now():
+ self.tasks.append(
+ SchedulerTask(
+ time=execute_time,
+ task_type=TaskTypes.SKILL_UPGRADE,
+ meta_data=skill,
+ )
+ )
+ return
+ else:
+ del tasks[0]
+ if tasks[0] == "upgrade":
+ # 进入技能选择界面
+ self.tap(
+ (self.recog.w * 0.05, self.recog.h * 0.95),
+ interval=0.5,
+ )
+ if tasks[0] == "confirm":
+ # 读取专精倒计时 如果没有,判定专精失败
+ execute_time = self.double_read_time(
+ ((236, 978), (380, 1020)), use_digit_reader=True
+ )
+ if execute_time < (datetime.now() + timedelta(hours=2)):
+ raise Exception(
+ "未获取专精时间倒计时,请确认技能专精材料充足"
+ )
+ else:
+ del tasks[0]
+ elif scene == Scene.TRAIN_SKILL_SELECT:
+ if tasks[0] == "upgrade":
+ # 点击技能
+ height = (int(skill) - 1) * 0.3 + 0.32
+ self.ctap((self.recog.w * 0.33, self.recog.h * height))
+ else:
+ self.back()
+ elif scene == Scene.TRAIN_SKILL_UPGRADE:
+ if tasks[0] == "upgrade":
+ # 根据剩余时间判定专精技能等级
+ finish_time = self.double_read_time(
+ (
+ (94, 998),
+ (223, 1048),
+ ),
+ use_digit_reader=True,
+ )
+ hours = (finish_time - datetime.now()).total_seconds() / 3600
+ if hours > 23:
+ level = 3
+ elif hours > 15:
+ level = 2
+ logger.info(f"本次专精将提升{skill}技能至{level}")
+ # 点击确认开始专精
+ self.tap((self.recog.w * 0.87, self.recog.h * 0.9))
+ del tasks[0]
+ else:
+ self.back()
+ elif scene == Scene.TRAIN_SKILL_UPGRADE_ERROR:
+ # 如果材料不满足则会出现错误
+ if tasks[0] == "confirm":
+ raise Exception("专精材料不足")
+ else:
+ self.back()
+ if len(self.op_data.skill_upgrade_supports) > 0:
+ support = next(
+ (
+ e
+ for e in self.op_data.skill_upgrade_supports
+ if e.level == level
+ ),
+ None,
+ )
+ h = self.op_data.calculate_switch_time(support)
+ if support is not None:
+ self.tasks.append(
+ SchedulerTask(task_plan={"train": [support.name, "Current"]})
+ )
+ # 提前10分钟换人,确保触发技能
+ # 3 级不需要换人
+ if level != 3:
+ swap_time = (
+ datetime.now() + timedelta(hours=h) - timedelta(minutes=10)
+ )
+ self.tasks.append(
+ SchedulerTask(
+ time=swap_time,
+ task_plan={"train": [support.swap_name, "Current"]},
+ )
+ )
+ self.tasks.append(
+ SchedulerTask(
+ time=swap_time + timedelta(seconds=1),
+ task_plan={},
+ task_type=TaskTypes.REFRESH_TIME,
+ meta_data="train",
+ )
+ )
+ # 默认 5小时
+ self.tasks.append(
+ SchedulerTask(
+ time=datetime.now()
+ + timedelta(hours=h + 5)
+ + timedelta(minutes=15),
+ task_plan={},
+ task_type=TaskTypes.SKILL_UPGRADE,
+ meta_data=skill,
+ )
+ )
+ else:
+ self.tasks.append(
+ SchedulerTask(
+ time=execute_time,
+ task_plan={},
+ task_type=TaskTypes.SKILL_UPGRADE,
+ meta_data=skill,
+ )
+ )
+ self.back()
+ except Exception as e:
+ logger.exception(e)
+ send_message("专精任务失败" + str(e), level="ERROR")
+
+ def plan_run_order(self, room):
+ plan = self.op_data.plan
+ if self.find_next_task(meta_data=room, task_type=TaskTypes.RUN_ORDER):
return
- if len(self.check_in_and_out()) > 0:
- # 处理龙舌兰和但书的插拔
- for room in self.check_in_and_out():
- if any(room in obj["plan"].keys() and 'type' not in obj.keys() for obj in self.tasks): continue;
- in_out_plan = {}
- in_out_plan[room] = ['Current'] * len(plan[room])
- for idx, x in enumerate(plan[room]):
- if '但书' in x['replacement'] or '龙舌兰' in x['replacement']:
- in_out_plan[room][idx] = x['replacement'][0]
- self.tasks.append({"time": self.get_in_and_out_time(room), "plan": in_out_plan})
+ in_out_plan = {room: ["Current"] * len(plan[room])}
+ for idx, x in enumerate(plan[room]):
+ if any(
+ any(char in replacement_str for replacement_str in x.replacement)
+ for char in ["但书", "龙舌兰", "佩佩"]
+ ):
+ in_out_plan[room][idx] = x.replacement[0]
+ self.tasks.append(
+ SchedulerTask(
+ time=self.get_run_roder_time(room),
+ task_plan=in_out_plan,
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data=room,
+ )
+ )
+
+ def plan_solver(self):
+ plan = self.op_data.plan
+ if len(self.op_data.run_order_rooms) > 0:
+ # 判定宿舍是否满员
+ valid = True
+ for key in plan.keys():
+ if "dormitory" in key:
+ dorm = self.op_data.get_current_room(key)
+ if dorm is not None and len(dorm) == 5:
+ continue
+ else:
+ valid = False
+ logger.info("宿舍未满员,跳过读取插拔时间")
+ break
+ if valid:
+ # 处理龙舌兰/但书/佩佩的插拔
+ for k, v in self.op_data.run_order_rooms.items():
+ self.plan_run_order(k)
+ adj_task = scheduling(self.tasks)
+ max_execution = 3
+ adj_count = 0
+ while adj_task is not None and adj_count < max_execution:
+ self.drone(adj_task.meta_data, adjust_time=True)
+ adj_task = scheduling(self.tasks)
+ adj_count += 1
# 准备数据
- if self.read_mood:
- # 根据剩余心情排序
- self.total_agent.sort(key=lambda x: x["mood"], reverse=False)
- # 目前有换班的计划后面改
- logger.debug(f'当前基地数据--> {self.total_agent}')
- exclude_list = []
- fia_plan, fia_room = self.check_fia()
- if fia_room is not None and fia_plan is not None:
- exclude_list = copy.deepcopy(fia_plan)
- if not any(fia_room in obj["plan"].keys() and len(obj["plan"][fia_room]) == 2 for obj in self.tasks):
- fia_idx = self.operators['菲亚梅塔']['index']
- result = [{}]*(fia_idx+1)
- result[fia_idx]['time'] =datetime.now()
- if self.operators["菲亚梅塔"]["mood"] != 24:
+ logger.debug(self.op_data.print())
+ # 根据剩余心情排序
+ self.total_agent = list(
+ v
+ for k, v in self.op_data.operators.items()
+ if v.is_high() and not v.room.startswith("dorm")
+ )
+ self.total_agent.sort(key=lambda x: x.current_mood(), reverse=False)
+ # 目前有换班的计划后面改
+ logger.debug(f"当前基地数据--> {self.total_agent}")
+ fia_plan, fia_room = self.check_fia()
+ if fia_room is not None and fia_plan is not None:
+ if self.find_next_task(task_type=TaskTypes.FIAMMETTA) is None:
+ fia_data = self.op_data.operators["菲亚梅塔"]
+ fia_idx = (
+ fia_data.current_index
+ if fia_data.current_index != -1
+ else fia_data.index
+ )
+ result = [{}] * (fia_idx + 1)
+ result[fia_idx]["time"] = datetime.now()
+ if fia_data.mood != 24:
+ if (
+ fia_data.time_stamp is not None
+ and fia_data.time_stamp > datetime.now()
+ ):
+ result[fia_idx]["time"] = fia_data.time_stamp
+ else:
self.enter_room(fia_room)
result = self.get_agent_from_room(fia_room, [fia_idx])
self.back()
- logger.info('下一次进行菲亚梅塔充能:' + result[fia_idx]['time'].strftime("%H:%M:%S"))
- self.tasks.append({"time": result[fia_idx]['time'], "plan": {fia_room: [
- next(obj for obj in self.total_agent if obj["agent"] in fia_plan)["agent"],
- "菲亚梅塔"]}})
- exclude_list.append("菲亚梅塔")
- try:
- exaust_rest = []
- if len(self.exaust_agent) > 0:
- for _exaust in self.exaust_agent:
- i = self.operators[_exaust]
- if 'current_room' in i.keys() and i['current_room'].startswith('dormitory') and i[
- 'resting_priority'] == 'high' and next(
- (k for k in self.tasks if 'type' in k.keys() and i['current_room'] in k["type"]),
- None) is None:
- exaust_rest.append(i['name'])
- if _exaust not in exclude_list:
- exclude_list.append(_exaust)
- # 如果exaust_agent<2 则读取
- logger.info(f'安排干员黑名单为:{exclude_list}')
- # 先计算需要休息满的人
- total_exaust_plan = []
- for agent in exaust_rest:
- error_count = 0
- time_result = None
- # 读取时间
- __index = self.operators[agent]['current_index']
- while error_count < 3:
- try:
- self.enter_room(self.operators[agent]['current_room'])
- time_result = self.get_agent_from_room(self.operators[agent]['current_room'],[__index])
- if time_result is None:
- raise Exception("读取时间失败")
- else:
+ logger.info(
+ "下一次进行菲亚梅塔充能:"
+ + result[fia_idx]["time"].strftime("%H:%M:%S")
+ )
+ self.tasks.append(
+ SchedulerTask(
+ time=result[fia_idx]["time"], task_type=TaskTypes.FIAMMETTA
+ )
+ )
+ try:
+ # 重新排序
+ if self.find_next_task(task_type=TaskTypes.SHIFT_OFF):
+ logger.info("有未完成的下班任务")
+ return
+ self.total_agent.sort(
+ key=lambda x: x.current_mood() - x.lower_limit, reverse=False
+ )
+ # 自动生成任务
+ self.plan_metadata()
+ # 剩余高效组位置
+ high_free = self.op_data.available_free()
+ # 剩余低效位置
+ low_free = self.op_data.available_free("low")
+ _replacement = []
+ _plan = {}
+ for op in self.total_agent:
+ # 忽略掉菲亚梅塔充能的干员
+ if high_free == 0 and low_free == 0:
+ break
+ if op.name in self.op_data.workaholic_agent:
+ continue
+ # 忽略掉正在休息的
+ if (
+ op.is_resting()
+ or op.current_room in ["factory", "train"]
+ or op.room in ["factory", "train"]
+ ):
+ continue
+ # 忽略掉心情值没低于上限的的
+ if op.current_mood() > int(
+ (op.upper_limit - op.lower_limit)
+ * self.op_data.config.resting_threshold
+ + op.lower_limit
+ ):
+ continue
+ if op.name in self.op_data.exhaust_agent:
+ if op.current_mood() <= op.lower_limit + 2:
+ if (
+ self.find_next_task(
+ task_type=TaskTypes.EXHAUST_OFF, meta_data=op.name
+ )
+ is None
+ ):
+ self.enter_room(op.current_room)
+ result = self.get_agent_from_room(
+ op.current_room, [op.current_index]
+ )
+ _time = datetime.now()
+ if (
+ result[op.current_index]["time"] is not None
+ and result[op.current_index]["time"] > _time
+ ):
+ _time = result[op.current_index]["time"] - timedelta(
+ minutes=10
+ )
+ elif (
+ op.current_mood() > 0.25 + op.lower_limit
+ and op.depletion_rate != 0
+ ):
+ _time = (
+ datetime.now()
+ + timedelta(
+ hours=(
+ op.current_mood() - op.lower_limit - 0.25
+ )
+ / op.depletion_rate
+ )
+ - timedelta(minutes=10)
+ )
+ self.back()
+ # plan 是空的是因为得动态生成
+ update_time = False
+ if op.group != "":
+ # 检查是否有其他同组任务,刷新时间
+ for item in self.op_data.groups[op.group]:
+ if item not in self.op_data.exhaust_agent:
+ continue
+ elif self.find_next_task(
+ task_type=TaskTypes.EXHAUST_OFF, meta_data=item
+ ):
+ update_time = True
+ exh_task = self.find_next_task(
+ task_type=TaskTypes.EXHAUST_OFF,
+ meta_data=item,
+ )
+ if _time < exh_task.time:
+ logger.info(
+ f"检测到用尽同组{op.name}比{item}提前下班,更新任务时间为{_time}"
+ )
+ exh_task.time = _time
+ exh_task.meta_data += f",{op.name}"
+ logger.debug(
+ f"更新用尽meta_data为{exh_task.meta_data}"
+ )
+ if not update_time:
+ self.tasks.append(
+ SchedulerTask(
+ time=_time,
+ task_type=TaskTypes.EXHAUST_OFF,
+ meta_data=op.name,
+ )
+ )
+ # 如果是生成的过去时间,则停止 plan 其他
+ if _time < datetime.now():
break
- except Exception as e:
- self.back_to_index()
- if self.scene() == Scene.INDEX:
- self.tap_element('index_infrastructure', interval=5)
- self.recog.update()
- logger.exception(e)
- error_count += 1
- continue
- # 5分钟gap
- time_result = time_result[__index]['time'] - timedelta(seconds=(300))
- # 如果已经有现有plan 则比对时间
- if next((k for k in total_exaust_plan if
- next((k for k, v in k['plan'].items() if agent in v), None) is not None),
- None) is not None:
- _exaust_plan = next(
- k for k in total_exaust_plan if next((k for k, v in k['plan'].items() if agent in v), None))
- if self.operators[agent]['current_room'] not in _exaust_plan['type']:
- _exaust_plan['type'] += ',' + self.operators[agent]['current_room']
- if time_result > _exaust_plan['time']:
- _exaust_plan['time'] = time_result
+ continue
+ if op.group != "":
+ if op.group in self.op_data.exhaust_group:
+ # 忽略掉用尽心情的分组
continue
- exaust_plan = {'plan': {}, 'time': time_result, 'type': self.operators[agent]['current_room']}
- # 如果有组,则生成小组上班 否则则单人上班
- bundle = []
- if self.operators[agent]['group'] != '':
- bundle.extend([v['name'] for k, v in self.operators.items() if
- 'group' in v.keys() and v['group'] == self.operators[agent]['group']])
- else:
- bundle.append(agent)
- for planned in bundle:
- if self.operators[planned]['room'] not in exaust_plan['plan']:
- exaust_plan['plan'][self.operators[planned]['room']] = [
- 'Current'] * len(
- self.currentPlan[self.operators[planned]['room']])
- exaust_plan['plan'][self.operators[planned]['room']][
- self.operators[planned]['index']] = planned
- total_exaust_plan.append(exaust_plan)
- self.back()
- self.tasks.extend(total_exaust_plan)
- resting_dorm = []
- for task in self.tasks:
- if 'type' in task.keys() and task['type'].startswith("dorm"):
- resting_dorm.extend(task["type"].split(','))
- actuall_resting = len(resting_dorm)
-
- if len(resting_dorm) < self.dorm_count:
- need_to_rest = []
- # 根据使用宿舍数量,输出需要休息的干员列表
- number_of_dorm = self.dorm_count
-
- min_mood = -99
- for agent in self.total_agent:
- if actuall_resting >= number_of_dorm:
- if min_mood == -99:
- min_mood = agent['mood']
+
+ self.rearrange_resting_priority(op.group)
+ # 如果在group里则同时上下班
+ group_resting = self.op_data.groups[op.group]
+
+ skip_resting = False
+ for operator in group_resting:
+ if self.op_data.operators[
+ operator
+ ].resting_priority == "high" and (
+ self.op_data.operators[operator].upper_limit
+ - self.op_data.operators[operator].current_mood()
+ < 2
+ ):
+ skip_resting = True
break
- if (len([value for value in need_to_rest if value["agent"] == agent["agent"]]) > 0):
- continue
- # 心情耗尽组如果心情 小于2 则记录时间
- if agent['agent'] in self.exaust_agent and agent['mood'] < 3 and not \
- self.operators[agent['agent']]['current_room'].startswith('dormitory'):
- if next((e for e in self.tasks if 'type' in e.keys() and e['type'] == agent['agent']),
- None) is None:
- __agent = self.operators[agent['agent']]
- self.enter_room(__agent['current_room'])
- result = self.get_agent_from_room(__agent['current_room'], [__agent['current_index']])
- self.back()
- # plan 是空的是因为得动态生成
- self.tasks.append({"time": result[__agent['current_index']]['time'], "plan": {}, "type": __agent['name']})
- else:
- continue
- # 忽略掉菲亚梅塔充能的干员
- if agent['agent'] in exclude_list:
- continue
- # 忽略掉低效率的干员
- if agent['agent'] in self.operators.keys() and self.operators[agent['agent']]['type'] != 'high':
- continue
- if 'resting_priority' in self.operators.keys() and self.operators[agent['agent']]['resting_priority'] != 'high':
- continue
- # 忽略掉正在休息的
- if agent['current_room'] in resting_dorm or agent['current_room'] in ['factory']:
- continue
- # 忽略掉心情值没低于上限的8的
- if agent['mood'] > int(self.operators[agent['agent']]["upper_limit"] * self.resting_treshhold):
- continue
- if agent['agent'] in self.operators.keys() and self.operators[agent['agent']]['group'] != '':
- # 如果在group里则同时上下班
- group_resting = [x for x in self.total_agent if
- self.operators[x['agent']]['group'] == self.operators[agent['agent']][
- 'group']]
- group_restingCount = 0
- for x in group_resting:
- if self.operators[x['agent']]['resting_priority'] == 'low':
- continue
- else:
- group_restingCount += 1
- if group_restingCount + actuall_resting <= self.dorm_count:
- need_to_rest.extend(group_resting)
- actuall_resting += group_restingCount
- else:
- # 因为人数不够而跳过记录心情
- min_mood = agent['mood']
- continue
- else:
- need_to_rest.append(agent)
- actuall_resting += 1
- # 输出转换后的换班plan
- logger.info(f'休息人选为->{need_to_rest}')
- if len(need_to_rest) > 0:
- self.get_swap_plan(resting_dorm, need_to_rest, min_mood < 3 and min_mood != -99)
- # 如果下个 普通任务 >5 分钟则补全宿舍
- if (next((e for e in self.tasks if e['time'] < datetime.now() + timedelta(seconds=300)),
- None)) is None:
- self.agent_get_mood()
- except Exception as e:
- logger.exception(f'计算排班计划出错->{e}')
-
- def get_swap_plan(self, resting_dorm, operators, skip_read_time):
- result = {}
- agents = copy.deepcopy(operators)
- # 替换计划
- for a in operators:
- if a['current_room'] not in result.keys():
- result[a['current_room']] = ['Current'] * len(self.currentPlan[a['current_room']])
- # 获取替换组且没有在上班的 排除但书或者龙舌兰
- __replacement = next((obj for obj in self.operators[a['agent']]['replacement'] if (not (
- self.operators[obj]['current_room'] != '' and not self.operators[obj][
- 'current_room'].startswith('dormitory'))) and obj not in ['但书', '龙舌兰']), None)
- if __replacement is not None:
- self.operators[__replacement]['current_room'] = a['current_room']
- result[a['current_room']][a['room_index']] = __replacement
- else:
- raise Exception(f"{a['agent']} 没有足够的替换组可用")
- group_info = {}
- read_time_rooms = []
- need_recover_room = []
- # 从休息计划里 规划出排班计划 并且立刻执行
- for room in [k for k, v in self.currentPlan.items() if (k not in resting_dorm) and k.startswith('dormitory')]:
- # 记录房间可以放几个干员:
- dorm_plan = [data["agent"] for data in self.currentPlan[room]]
- # 塞一个最优组进去
- next_agent = next((obj for obj in agents if self.operators[obj["agent"]]['resting_priority'] == 'high'),
- None)
- planned_agent = []
- if next_agent is not None:
- dorm_plan[dorm_plan.index('Free')] = next_agent["agent"]
- planned_agent.append(next_agent["agent"])
- agents.remove(next_agent)
- else:
- break
- if skip_read_time:
- if 'rest_in_full' in self.operators[next_agent['agent']].keys() and \
- self.operators[next_agent['agent']]["rest_in_full"]:
- need_recover_room.append(room)
- read_time_rooms.append((room))
- else:
- if 'rest_in_full' in self.operators[next_agent['agent']].keys() and \
- self.operators[next_agent['agent']]["rest_in_full"]:
- skip_read_time = True
- read_time_rooms.append(room)
- free_num = dorm_plan.count('Free')
- while free_num > 0:
- next_agent_low = next(
- (obj for obj in agents if self.operators[obj["agent"]]['resting_priority'] == 'low'), None)
- if next_agent_low is not None:
- dorm_plan[dorm_plan.index('Free')] = next_agent_low["agent"]
- planned_agent.append(next_agent_low["agent"])
- agents.remove(next_agent_low)
- free_num -= 1
- else:
- break
- result[room] = dorm_plan
- # 如果有任何正在休息的最优组
- for item in self.current_base[room]:
- if 'agent' in item.keys():
- _item_name = item['agent']
- if _item_name in self.operators.keys() and self.operators[_item_name]['type'] == 'high' and not \
- self.operators[_item_name]['room'].startswith(
- 'dormitory'):
- # 如果是高效干员
- _room = self.operators[_item_name]['room']
- if _room not in result.keys():
- result[_room] = ['Current'] * len(self.currentPlan[_room])
- result[_room][self.operators[_item_name]['index']] = _item_name
- if room in need_recover_room:
- group_name = self.operators[next_agent['agent']]["name"]
- else:
- # 未分组则强制分组
- group_name = 'default'
- if not group_name in group_info.keys():
- group_info[group_name] = {'type': room, 'plan': {}}
- else:
- group_info[group_name]['type'] += ',' + room
- for planned in planned_agent:
- if self.operators[planned]['current_room'] not in group_info[group_name]['plan']:
- group_info[group_name]['plan'][self.operators[planned]['current_room']] = ['Current'] * len(
- self.currentPlan[self.operators[planned]['room']])
- group_info[group_name]['plan'][self.operators[planned]['current_room']][
- self.operators[planned]['index']] = planned
- # group_info[group_name]['plan'][room]=[x['agent'] for x in self.currentPlan[room]]
- logger.info(f'生成的分组计划:{group_info}')
- logger.info(f'生成的排班计划为->{result}')
- self.tasks.append(
- {'plan': result, 'time': datetime.now(), 'metadata': {'plan': group_info, 'room': read_time_rooms}})
-
- def get_agent(self):
- plan = self.currentPlan
- high_production = []
- replacements = []
- for room in plan.keys():
- for idx, data in enumerate(plan[room]):
- __high = {"name": data["agent"], "room": room, "index": idx, "group": data["group"],
- 'replacement': data["replacement"], 'resting_priority': 'high', 'current_room': '',
- 'exhaust_require': False, "upper_limit": 24,"rest_in_full":False}
- if __high['name'] in self.agent_base_config.keys() and 'RestingPriority' in self.agent_base_config[
- __high['name']].keys() and self.agent_base_config[__high['name']]['RestingPriority'] == 'low':
- __high["resting_priority"] = 'low'
- if __high['name'] in self.agent_base_config.keys() and 'ExhaustRequire' in self.agent_base_config[
- __high['name']].keys() and self.agent_base_config[__high['name']]['ExhaustRequire'] == True:
- __high["exhaust_require"] = True
- if __high['name'] in self.agent_base_config.keys() and 'UpperLimit' in self.agent_base_config[
- __high['name']].keys():
- __high["upper_limit"] = self.agent_base_config[__high['name']]['UpperLimit']
- if __high['name'] in self.agent_base_config.keys() and 'RestInFull' in self.agent_base_config[
- __high['name']].keys() and self.agent_base_config[__high['name']]['RestInFull'] == True:
- __high["rest_in_full"] = True
- high_production.append(__high)
- if "replacement" in data.keys() and data["agent"] != '菲亚梅塔':
- replacements.extend(data["replacement"])
- for agent in high_production:
- if agent["room"].startswith('dormitory'):
- agent["type"] = "low"
- else:
- agent["type"] = "high"
- self.operators[agent["name"]] = agent
- for agent in replacements:
- if agent in self.operators.keys():
- if 'type' in self.operators[agent].keys() and self.operators[agent]['type'] == 'high':
- continue
+ if skip_resting:
+ logger.debug(
+ f"{op.group}组内干员{operator}的心情{self.op_data.operators[operator].current_mood()}过高,跳过休息"
+ )
+ continue
+
+ _replacement, _plan, high_free, low_free = self.get_resting_plan(
+ group_resting, _replacement, _plan, high_free, low_free
+ )
else:
- self.operators[agent] = {"type": "low", "name": agent, "group": '', 'resting_priority': 'low',
- "index": -1, 'current_room': '', 'mood': 24, "upper_limit": 24,"rest_in_full":False}
+ _replacement, _plan, high_free, low_free = self.get_resting_plan(
+ [op.name], _replacement, _plan, high_free, low_free
+ )
+ if len(_plan.keys()) > 0:
+ self.tasks.append(
+ SchedulerTask(task_plan=_plan, task_type=TaskTypes.SHIFT_OFF)
+ )
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ # 如果下个 普通任务 >5 分钟则补全宿舍
+ logger.debug("tasks:" + str(self.tasks))
+ if self.find_next_task(datetime.now() + timedelta(seconds=15)):
+ logger.info("有其他任务,跳过宿舍纠错")
+ return
+ if self.agent_get_mood() is None:
+ self.backup_plan_solver()
+
+ def backup_plan_solver(self, timing=None):
+ if timing is None:
+ timing = PlanTriggerTiming.END
+ try:
+ if self.op_data.backup_plans:
+ con = copy.deepcopy(self.op_data.plan_condition)
+ current_con = self.op_data.plan_condition
+ new_task = False
+ for idx, bp in enumerate(self.op_data.backup_plans):
+ func = str(bp.trigger)
+ logger.debug(func)
+ con[idx] = self.op_data.evaluate_expression(func)
+ if (
+ current_con[idx] != con[idx]
+ and bp.trigger_timing.value <= timing.value
+ ):
+ task = self.op_data.backup_plans[idx].task
+ if task and con[idx]:
+ new_task = True
+ self.tasks.append(
+ SchedulerTask(task_plan=copy.deepcopy(task))
+ )
+ else:
+ # 不切换
+ con[idx] = current_con[idx]
+ # 不满足条件且为其他排班表,则切换回来
+ if con != current_con:
+ logger.info(
+ f"检测到副班条件变更,启动超级变换形态, 当前条件:{current_con}"
+ )
+ logger.info(f"新条件列表:{con}")
+ self.op_data.swap_plan(con, refresh=True)
+ if not new_task:
+ self.tasks.append(SchedulerTask(task_plan={}))
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+
+ def rearrange_resting_priority(self, group):
+ operators = self.op_data.groups[group]
+ # 肥鸭充能新模式:https://github.com/ArkMowers/arknights-mower/issues/551
+ fia_plan, fia_room = self.check_fia()
+ # 排序
+ # 1. 肥鸭充能列表中的干员靠前
+ # 2. 不在加工站的干员靠前
+ # 3. 心情低的干员靠前
+ operators.sort(
+ key=lambda y: (
+ y not in fia_plan if fia_plan else True,
+ self.op_data.operators[y].current_room in ["factory", "train"],
+ self.op_data.operators[y].current_mood()
+ - self.op_data.operators[y].lower_limit,
+ )
+ )
+
+ high_count = 0
+ for operator in operators:
+ if self.op_data.operators[operator].workaholic:
+ continue
+ if self.op_data.operators[operator].resting_priority == "high":
+ high_count += 1
+ for operator in operators:
+ if self.op_data.operators[operator].workaholic:
+ continue
+ self.op_data.operators[operator].resting_priority = (
+ "high" if high_count > 0 else "low"
+ )
+ high_count -= 1
+
+ def get_resting_plan(self, agents, exist_replacement, plan, high_free, low_free):
+ _low, _high = 0, 0
+ __replacement = []
+ __plan = {}
+ for x in agents:
+ if self.op_data.operators[x].workaholic:
+ continue
+ if self.op_data.operators[x].resting_priority == "low":
+ _low += 1
else:
- self.operators[agent] = {"type": "low", "name": agent, "group": '', 'current_room': '',
- 'resting_priority': 'low', "index": -1, 'mood': 24, "upper_limit": 24,"rest_in_full":False}
- self.exaust_agent = []
- if next((k for k, v in self.operators.items() if 'exhaust_require' in v.keys() and v["exhaust_require"]),
- None) is not None:
- exhaust_require = [v for k, v in self.operators.items() if
- 'exhaust_require' in v.keys() and v["exhaust_require"]]
- for i in exhaust_require:
- if i['name'] in self.exaust_agent: continue
- if 'group' in i.keys() and i['group'] != '':
- self.exaust_agent.extend([v['name'] for k, v in self.operators.items() if
- 'group' in v.keys() and v['group'] == i['group']])
+ _high += 1
+ logger.debug(f"需求高效:{_high},低效:{_low}")
+ # 肥鸭充能新模式:https://github.com/ArkMowers/arknights-mower/issues/551
+ fia_plan, fia_room = self.check_fia()
+ # 排序
+ # 1. 肥鸭充能列表中的干员靠前
+ # 2. 不在加工站的干员靠前
+ # 3. 心情低的干员靠前
+ agents.sort(
+ key=lambda y: (
+ y not in fia_plan if fia_plan else True,
+ self.op_data.operators[y].current_room in ["factory", "train"],
+ self.op_data.operators[y].current_mood()
+ - self.op_data.operators[y].lower_limit,
+ )
+ )
+ # 进行位置数量的初步判定
+ # 对于252可能需要进行额外判定,由于 low_free 性质等同于 high_free
+ success = True
+ if high_free - _high >= 0 and low_free - _low >= 0:
+ for agent in agents:
+ if not success:
+ break
+ x = self.op_data.operators[agent]
+ if self.op_data.get_dorm_by_name(x.name)[0] is not None:
+ # 如果干员已经被安排了
+ success = False
+ break
+ _rep = next(
+ (
+ obj
+ for obj in x.replacement
+ if (
+ not (
+ self.op_data.operators[obj].current_room != ""
+ and not self.op_data.operators[obj].is_resting()
+ )
+ )
+ and obj not in ["但书", "龙舌兰", "佩佩"]
+ and obj not in exist_replacement
+ and obj not in __replacement
+ and self.op_data.operators[obj].current_room != x.room
+ ),
+ None,
+ )
+ if _rep is not None:
+ __replacement.append(_rep)
+ if x.room not in __plan.keys():
+ __plan[x.room] = ["Current"] * len(self.op_data.plan[x.room])
+ __plan[x.room][x.index] = _rep
else:
- self.exaust_agent.append(i['name'])
- logger.info(f'需要用尽心情的干员为: {self.exaust_agent}')
-
- def check_in_and_out(self):
- res = {}
- for x, y in self.currentPlan.items():
- if not x.startswith('room'): continue
- if any(('但书' in obj['replacement'] or '龙舌兰' in obj['replacement']) for obj in y):
- res[x] = y
- return res
+ success = False
+ if success:
+ # 记录替换组
+ exist_replacement.extend(__replacement)
+ new_plan = False
+ if any(
+ self.op_data.operators[a_name].resting_priority == "low"
+ for a_name in agents
+ ):
+ first_low = last_high = None
+ for a in agents:
+ ag = self.op_data.operators[a]
+ if ag.workaholic:
+ continue
+ if ag.resting_priority == "low" and first_low is None:
+ first_low = ag
+ if ag.resting_priority == "high":
+ last_high = ag
+ # 如果低优先的心情低于高优先
+ if first_low is None or last_high is None:
+ pass
+ elif (
+ first_low.current_mood() - last_high.current_mood()
+ < (last_high.lower_limit - last_high.upper_limit) / 8
+ ):
+ logger.info("低优先级干员心情过低,自动按心情切换优先级")
+ new_plan = True
+ workaholic_count = 0
+ for idx, x in enumerate(agents):
+ if self.op_data.operators[x].workaholic:
+ workaholic_count += 1
+ continue
+ if new_plan:
+ self.op_data.operators[x].resting_priority = (
+ "high" if idx + 1 - workaholic_count <= _high else "low"
+ )
+ logger.info(
+ f"自动更新{x} 优先级为 {self.op_data.operators[x].resting_priority}"
+ )
+ _dorm = self.op_data.assign_dorm(x)
+ if _dorm.position[0] not in plan.keys():
+ plan[_dorm.position[0]] = ["Current"] * 5
+ plan[_dorm.position[0]][_dorm.position[1]] = _dorm.name
+ for k, v in __plan.items():
+ if k not in plan.keys():
+ plan[k] = __plan[k]
+ for idx, name in enumerate(__plan[k]):
+ if plan[k][idx] == "Current" and name != "Current":
+ plan[k][idx] = name
+ else:
+ success = False
+ if not success:
+ _high, _low = 0, 0
+ else:
+ # 如果组内心情人差距过大,则报错
+ low_mood = 24
+ high_mood = 0
+ low_name = ""
+ high_name = ""
+ for agent in agents:
+ x = self.op_data.operators[agent]
+ if x.resting_priority == "high" and not x.workaholic:
+ mood = 24 - x.upper_limit + x.current_mood()
+ if mood < low_mood:
+ low_mood = mood + 0
+ low_name = agent
+ if mood > high_mood:
+ high_mood = mood + 0
+ high_name = agent
+ logger.debug(f"低心情:{low_mood}")
+ logger.debug(f"高心情:{high_mood}")
+ if low_mood + 4 <= high_mood:
+ low_agent = self.op_data.operators[low_name]
+ if not low_agent.rest_in_full:
+ msg = f"同组干员{low_name}与{high_name}心情差值大于4,请注意!"
+ logger.warning(msg)
+ send_message(msg, level="WARNING")
+ return exist_replacement, plan, high_free - _high, low_free - _low
+
+ def initialize_operators(self):
+ self.op_data = Operators(self.global_plan)
+ Operators.current_room_changed_callback = self.current_room_changed
+ return self.op_data.init_and_validate()
def check_fia(self):
- res = {}
- if '菲亚梅塔' in self.operators.keys() and self.operators['菲亚梅塔']['room'].startswith('dormitory'):
- if 'replacement' in self.operators['菲亚梅塔'].keys():
- return self.operators['菲亚梅塔']['replacement'], self.operators['菲亚梅塔']['room']
+ if "菲亚梅塔" in self.op_data.operators.keys() and self.op_data.operators[
+ "菲亚梅塔"
+ ].room.startswith("dormitory"):
+ return self.op_data.operators[
+ "菲亚梅塔"
+ ].replacement, self.op_data.operators["菲亚梅塔"].room
return None, None
- def get_in_and_out_time(self, room):
- logger.info('基建:读取插拔时间')
+ def get_run_roder_time(self, room):
+ logger.info("基建:读取插拔时间")
# 点击进入该房间
self.enter_room(room)
# 进入房间详情
error_count = 0
- while self.find('bill_accelerate') is None:
+ while self.find("bill_accelerate") is None:
if error_count > 5:
- raise Exception('未成功进入无人机界面')
+ raise Exception("未成功进入无人机界面")
self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=1)
error_count += 1
- execute_time = self.double_read_time((int(self.recog.w * 650 / 2496), int(self.recog.h * 660 / 1404),
- int(self.recog.w * 815 / 2496), int(self.recog.h * 710 / 1404)))
- execute_time = execute_time - timedelta(seconds=(600))
- logger.info('下一次进行插拔的时间为:' + execute_time.strftime("%H:%M:%S"))
- logger.info('返回基建主界面')
- self.back(interval=2, rebuild=False)
- self.back(interval=2)
+ execute_time = self.double_read_time(
+ (
+ (int(self.recog.w * 650 / 2496), int(self.recog.h * 660 / 1404)),
+ (int(self.recog.w * 815 / 2496), int(self.recog.h * 710 / 1404)),
+ ),
+ use_digit_reader=True,
+ )
+ execute_time = execute_time - timedelta(
+ seconds=(60 * config.conf.run_order_delay)
+ )
+ logger.info("下一次进行插拔的时间为:" + execute_time.strftime("%H:%M:%S"))
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
return execute_time
- def double_read_time(self, cord, upperLimit=36000, error_count=0,):
- if upperLimit < 36000:
- upperLimit = 36000
- self.recog.update()
- time_in_seconds = self.read_time(cord, upperLimit)
- execute_time = datetime.now() + timedelta(seconds=(time_in_seconds))
- return execute_time
-
-
- def initialize_paddle(self):
- global ocr
- if ocr is None:
- ocr = PaddleOCR(use_angle_cls=True, lang='en')
-
- def read_screen(self,img, type="mood",limit=24, cord=None, change_color=False):
- if cord is not None:
- img = img[cord[1]:cord[3], cord[0]:cord[2]]
- if 'mood' in type or type == "time":
- # 心情图片太小,复制8次提高准确率
- for x in range(0, 4):
- img = cv2.vconcat([img, img])
- if change_color: img[img == 137] = 255
- try:
- self.initialize_paddle()
- rets = ocr.ocr(img, cls=True)
- line_conf = []
- for idx in range(len(rets[0])):
- res = rets[0][idx]
- if 'mood' in type :
- # filter 掉不符合规范的结果
- if ('/' + str(limit)) in res[1][0]:
- line_conf.append(res[1])
- else:
- line_conf.append(res[1])
- logger.debug(line_conf)
- if len(line_conf) == 0 and 'mood' in type: return -1
- x = [i[0] for i in line_conf]
- __str = max(set(x), key=x.count)
- print(__str)
- if "mood" in type:
- number = int(__str[0:__str.index('/')])
- return number
- elif 'time' in type:
- if '.' in __str:
- __str = __str.replace(".", ":")
- return __str
- except Exception as e :
- logger.exception(e)
- return limit
-
- def read_time(self, cord, upperlimit, error_count=0):
- # 刷新图片
- self.recog.update()
- time_str = segment.read_screen(self.recog.img, type='time', cord=cord)
- logger.debug(str(time_str))
- try:
- h, m, s = str(time_str).split(':')
- if int(m)>60 or int(s)>60:
- raise Exception(f"读取错误")
- res = int(h) * 3600 + int(m) * 60 + int(s)
- if res>upperlimit:
- raise Exception(f"超过读取上限")
- else :return res
- except:
- logger.error("读取失败" + "--> " + str(time_str))
- if error_count > 50:
- raise Exception(f"读取失败{error_count}次超过上限")
- else:
- return self.read_time(cord,upperlimit, error_count + 1)
-
def todo_list(self) -> None:
- """ 处理基建 Todo 列表 """
+ """处理基建 Todo 列表"""
tapped = False
- trust = self.find('infra_collect_trust')
- if trust is not None:
- logger.info('基建:干员信赖')
- self.tap(trust)
- tapped = True
- bill = self.find('infra_collect_bill')
- if bill is not None:
- logger.info('基建:订单交付')
- self.tap(bill)
- tapped = True
- factory = self.find('infra_collect_factory')
- if factory is not None:
- logger.info('基建:可收获')
- self.tap(factory)
- tapped = True
+ collect = {"bill": "订单", "factory": "制造站产物", "trust": "信赖"}
+ for res, name in collect.items():
+ tap_times = 0
+ while pos := self.find(f"infra_collect_{res}"):
+ logger.info(f"收取{name}")
+ self.tap(pos)
+ tapped = True
+ tap_times += 1
+ if tap_times > 5:
+ break
if not tapped:
- self.tap((self.recog.w * 0.05, self.recog.h * 0.95))
+ # 点击右上角的通知图标
+ # 可能被产物收取提示挡住,所以直接点位置
+ self.tap((1840, 140))
self.todo_task = True
- def clue(self) -> None:
- # 一些识别时会用到的参数
- global x1, x2, x3, x4, y0, y1, y2
- x1, x2, x3, x4 = 0, 0, 0, 0
- y0, y1, y2 = 0, 0, 0
-
- logger.info('基建:线索')
-
- # 进入会客室
- self.enter_room('meeting')
-
- # 点击线索详情
- self.tap((self.recog.w * 0.1, self.recog.h * 0.9), interval=3)
-
- # 如果是线索交流的报告则返回
- self.find('clue_summary') and self.back()
-
- # 识别右侧按钮
- (x0, y0), (x1, y1) = self.find('clue_func', strict=True)
-
- logger.info('接收赠送线索')
- self.tap(((x0 + x1) // 2, (y0 * 3 + y1) // 4), interval=3, rebuild=False)
- self.tap((self.recog.w - 10, self.recog.h - 10), interval=3, rebuild=False)
- self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3, rebuild=False)
-
- logger.info('领取会客室线索')
- self.tap(((x0 + x1) // 2, (y0 * 5 - y1) // 4), interval=3)
- obtain = self.find('clue_obtain')
- if obtain is not None and self.get_color(self.get_pos(obtain, 0.25, 0.5))[0] < 20:
- self.tap(obtain, interval=2)
- if self.find('clue_full') is not None:
- self.back()
- else:
- self.back()
-
- logger.info('放置线索')
- clue_unlock = self.find('clue_unlock')
- if clue_unlock is not None:
- # 当前线索交流未开启
- self.tap_element('clue', interval=3)
-
- # 识别阵营切换栏
- self.recog_bar()
-
- # 点击总览
- self.tap(((x1 * 7 + x2) // 8, y0 // 2), rebuild=False)
-
- # 获得和线索视图相关的数据
- self.recog_view(only_y2=False)
-
- # 检测是否拥有全部线索
- get_all_clue = True
+ def clue_new(self):
+ logger.info("基建:线索")
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
+ self.enter_room("meeting")
+
+ clue_size = (162, 216)
+ clue_top_left = {
+ "daily": (1118, 334),
+ "receive": (1305, 122),
+ "give_away": (30, 208),
+ # 摆放线索界面,线索框的左上角
+ 1: (72, 228),
+ 2: (374, 334),
+ 3: (679, 198),
+ 4: (1003, 265),
+ 5: (495, 660),
+ 6: (805, 573),
+ 7: (154, 608),
+ }
+ dot_offset = (168, -8)
+ main_offset = (425, 0)
+ main_time_offset = (443, 257)
+
+ def va(a, b):
+ return a[0] + b[0], a[1] + b[1]
+
+ def tl2p(top_left):
+ return top_left, va(top_left, clue_size)
+
+ def is_orange(dot):
+ orange_dot = (255, 104, 1)
+ return all([abs(dot[i] - orange_dot[i]) < 3 for i in range(3)])
+
+ clue_scope = {}
+ for index, top_left in clue_top_left.items():
+ clue_scope[index] = tl2p(top_left)
+ clue_dots = {}
+ main_dots = {}
+ main_time = {}
+ main_scope = {}
+ for i in range(1, 8):
+ clue_dots[i] = va(clue_top_left[i], dot_offset)
+ main_dots[i] = va(clue_dots[i], main_offset)
+ main_time[i] = va(clue_top_left[i], main_time_offset)
+ main_scope[i] = tl2p(va(clue_top_left[i], main_offset))
+
+ class ClueTaskManager:
+ def __init__(self):
+ # 操作顺序:领取每日线索、接收好友线索、摆线索、送线索、更新线索交流结束时间
+ self.task_list = [
+ "daily",
+ "receive",
+ "place",
+ "give_away",
+ "party_time",
+ ]
+ self.task = self.task_list[0]
+
+ def complete(self, task):
+ task = task or self.task
+ if task in self.task_list:
+ self.task_list.remove(task)
+ self.task = self.task_list[0] if self.task_list else None
+
+ tm_thres = 0.6
+
+ def clue_cls(scope):
+ scope_dict = clue_scope if isinstance(scope, str) else main_scope
+ img = cropimg(self.recog.img, scope_dict[scope])
for i in range(1, 8):
- # 切换阵营
- self.tap(self.switch_camp(i), rebuild=False)
-
- # 清空界面内被选中的线索
- self.clear_clue_mask()
-
- # 获得和线索视图有关的数据
- self.recog_view()
+ res = loadres(f"clue/{i}")
+ result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if max_val > tm_thres:
+ return i
+ return None
- # 检测该阵营线索数量为 0
- if len(self.ori_clue()) == 0:
- logger.info(f'无线索 {i}')
- get_all_clue = False
- break
+ exit_pos = (1239, 144)
- # 检测是否拥有全部线索
- if get_all_clue:
- for i in range(1, 8):
- # 切换阵营
- self.tap(self.switch_camp(i), rebuild=False)
+ ctm = ClueTaskManager()
- # 获得和线索视图有关的数据
- self.recog_view()
+ friend_clue = []
- # 放置线索
- logger.info(f'放置线索 {i}')
- self.tap(((x1 + x2) // 2, y1 + 3), rebuild=False)
+ clue_status = {}
- # 返回线索主界面
- self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3, rebuild=False)
+ def place_index():
+ for cl, st in clue_status.items():
+ if st in ["available", "self", "available_self_only"]:
+ return cl, st
+ return None, None
- # 线索交流开启
- if clue_unlock is not None and get_all_clue:
- self.tap(clue_unlock)
- else:
- self.back(interval=2, rebuild=False)
-
- logger.info('返回基建主界面')
- self.back(interval=2)
-
- def switch_camp(self, id: int) -> tuple[int, int]:
- """ 切换阵营 """
- x = ((id + 0.5) * x2 + (8 - id - 0.5) * x1) // 8
- y = (y0 + y1) // 2
- return x, y
-
- def recog_bar(self) -> None:
- """ 识别阵营选择栏 """
- global x1, x2, y0, y1
-
- (x1, y0), (x2, y1) = self.find('clue_nav', strict=True)
- while int(self.recog.img[y0, x1 - 1].max()) - int(self.recog.img[y0, x1].max()) <= 1:
- x1 -= 1
- while int(self.recog.img[y0, x2].max()) - int(self.recog.img[y0, x2 - 1].max()) <= 1:
- x2 += 1
- while abs(int(self.recog.img[y1 + 1, x1].max()) - int(self.recog.img[y1, x1].max())) <= 1:
- y1 += 1
- y1 += 1
-
- logger.debug(f'recog_bar: x1:{x1}, x2:{x2}, y0:{y0}, y1:{y1}')
-
- def recog_view(self, only_y2: bool = True) -> None:
- """ 识别另外一些和线索视图有关的数据 """
- global x1, x2, x3, x4, y0, y1, y2
-
- # y2: 线索底部
- y2 = self.recog.h
- while self.recog.img[y2 - 1, x1:x2].ptp() <= 24:
- y2 -= 1
- if only_y2:
- logger.debug(f'recog_view: y2:{y2}')
- return y2
- # x3: 右边黑色 mask 边缘
- x3 = self.recog_view_mask_right()
- # x4: 用来区分单个线索
- x4 = (54 * x1 + 25 * x2) // 79
-
- logger.debug(f'recog_view: y2:{y2}, x3:{x3}, x4:{x4}')
-
- def recog_view_mask_right(self) -> int:
- """ 识别线索视图中右边黑色 mask 边缘的位置 """
- x3 = x2
- while True:
- max_abs = 0
- for y in range(y1, y2):
- max_abs = max(max_abs,
- abs(int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0])))
- if max_abs <= 5:
- x3 -= 1
- else:
- break
- flag = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0]) == max_abs:
- flag = True
- if not flag:
- self.tap(((x1 + x2) // 2, y1 + 10), rebuild=False)
- x3 = x2
- while True:
- max_abs = 0
- for y in range(y1, y2):
- max_abs = max(max_abs,
- abs(int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0])))
- if max_abs <= 5:
- x3 -= 1
- else:
- break
- flag = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0]) == max_abs:
- flag = True
- if not flag:
- x3 = None
- return x3
-
- def get_clue_mask(self) -> None:
- """ 界面内是否有被选中的线索 """
- try:
- mask = []
- for y in range(y1, y2):
- if int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0]) > 20 and np.ptp(
- self.recog.img[y, x3 - 2]) == 0:
- mask.append(y)
- if len(mask) > 0:
- logger.debug(np.average(mask))
- return np.average(mask)
- else:
+ def detect_unlock():
+ unlock_pos = self.find("clue/button_unlock")
+ if unlock_pos is None:
return None
- except Exception as e:
- raise RecognizeError(e)
+ color = self.get_color(self.get_pos(unlock_pos))
+ if all(color > [252] * 3):
+ return unlock_pos
+ return None
+
+ while ctm.task:
+ scene = self.scene()
+
+ if scene == Scene.INFRA_DETAILS:
+ if ctm.task == "party_time":
+ if self.find("clue/title_party", scope=((1600, 190), (1880, 260))):
+ self.party_time = self.double_read_time(
+ ((1768, 438), (1902, 480))
+ )
+ logger.info(f"线索交流结束时间:{self.party_time}")
+ if not find_next_task(
+ self.tasks,
+ task_type=TaskTypes.CLUE_PARTY,
+ ):
+ self.tasks.append(
+ SchedulerTask(
+ time=self.party_time - timedelta(milliseconds=1),
+ task_type=TaskTypes.CLUE_PARTY,
+ )
+ )
+ else:
+ self.party_time = None
+ logger.info("线索交流未开启")
+ ctm.complete("party_time")
+ else:
+ # 点击左下角,关闭进驻信息,进入线索界面
+ self.tap((725, 850))
+
+ elif scene == Scene.INFRA_CONFIDENTIAL:
+ if ctm.task == "daily":
+ # 检查是否领过线索
+ daily_scope = ((1815, 200), (1895, 250))
+ if self.find("clue/badge_new", scope=daily_scope):
+ self.tap((1800, 270))
+ else:
+ ctm.complete("daily")
+ elif ctm.task == "receive":
+ receive_scope = ((1815, 360), (1895, 410))
+ if self.find("clue/badge_new", scope=receive_scope):
+ self.ctap((1800, 430))
+ else:
+ ctm.complete("receive")
+ elif ctm.task == "place":
+ if unlock_pos := detect_unlock():
+ self.tap(unlock_pos)
+ continue
+ for i in range(1, 8):
+ if is_orange(self.get_color(main_dots[i])):
+ clue_status[i] = "available"
+ elif clue_cls(i):
+ hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180:
+ clue_status[i] = "friend"
+ else:
+ clue_status[i] = "self"
+ else:
+ clue_status[i] = None
+ cl, st = place_index()
+ if st in ["available", "self", "available_self_only"]:
+ self.tap(main_scope[cl])
+ continue
+ else:
+ ctm.complete("place")
+ elif ctm.task == "give_away":
+ self.ctap((1799, 578))
+ elif ctm.task == "party_time":
+ self.back()
- def clear_clue_mask(self) -> None:
- """ 清空界面内被选中的线索 """
- try:
- while True:
- mask = False
- for y in range(y1, y2):
- if int(self.recog.img[y, x3 - 1, 0]) - int(self.recog.img[y, x3 - 2, 0]) > 20 and np.ptp(
- self.recog.img[y, x3 - 2]) == 0:
- self.tap((x3 - 2, y + 1), rebuild=True)
- mask = True
- break
- if mask:
+ elif scene == Scene.CLUE_DAILY:
+ if not self.find(
+ "clue/icon_notification", scope=((1400, 0), (1920, 400))
+ ) and (clue := clue_cls("daily")):
+ logger.info(f"领取今日线索({clue}号)")
+ self.tap_element("clue/button_get")
+ ctm.complete("daily")
+ else:
+ # 今日线索已领取,点X退出
+ self.tap((1484, 152))
+
+ elif scene == Scene.CLUE_RECEIVE:
+ if self.find(
+ "infra_trust_complete", scope=((1230, 0), (1920, 1080)), score=0.1
+ ):
+ self.sleep()
continue
- break
- except Exception as e:
- raise RecognizeError(e)
-
- def ori_clue(self):
- """ 获取界面内有多少线索 """
- clues = []
- y3 = y1
- status = -2
- for y in range(y1, y2):
- if self.recog.img[y, x4 - 5:x4 + 5].max() < 192:
- if status == -1:
- status = 20
- if status > 0:
- status -= 1
- if status == 0:
- status = -2
- clues.append(segment.get_poly(x1, x2, y3, y - 20))
- y3 = y - 20 + 5
- else:
- status = -1
- if status != -2:
- clues.append(segment.get_poly(x1, x2, y3, y2))
-
- # 忽视一些只有一半的线索
- clues = [x.tolist() for x in clues if x[1][1] - x[0][1] >= self.recog.h / 5]
- logger.debug(clues)
- return clues
+ if clue := clue_cls("receive"):
+ name_scope = ((1580, 220), (1880, 255))
+ name_img = cropimg(self.recog.gray, name_scope)
+ name_img = cv2.copyMakeBorder(
+ name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
+ )
+ name = rapidocr.engine(
+ name_img,
+ use_det=True,
+ use_cls=False,
+ use_rec=True,
+ )[0][0][1]
+ name = name.strip() if name else "好友"
+ logger.info(f"接收{name}的{clue}号线索")
+ self.tap(name_scope)
+ else:
+ ctm.complete("receive")
+ self.tap(exit_pos)
+
+ elif scene == Scene.CLUE_PLACE:
+ cl, st = place_index()
+ if cl is None:
+ if unlock_pos := detect_unlock():
+ self.tap(unlock_pos)
+ else:
+ ctm.complete("place")
+ self.tap(exit_pos)
+ continue
+ if self.get_color((1328 + 77 * cl, 114))[0] < 150:
+ # 右上角 1-7
+ self.tap(clue_scope[cl])
+ continue
+ receive = st in ["available", "self"]
+ filter_receive = (1900, 45)
+ filter_self = (1610, 70)
+ filter_pos = filter_receive if receive else filter_self
+ if not all(self.get_color(filter_pos) > [252] * 3):
+ self.tap(filter_pos)
+ continue
+ clue_pos = ((1305, 208), (1305, 503), (1305, 797))
+ clue_list = []
+ for cp in clue_pos:
+ clue_img = cropimg(self.recog.img, tl2p(cp))
+ res = loadres(f"clue/{cl}")
+ result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if max_val > tm_thres:
+ name_scope = (va(cp, (274, 99)), va(cp, (580, 134)))
+ name_img = cropimg(self.recog.gray, name_scope)
+ name_img = cv2.copyMakeBorder(
+ name_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
+ )
+ name = rapidocr.engine(
+ name_img,
+ use_det=True,
+ use_cls=False,
+ use_rec=True,
+ )[0][0][1]
+ if name:
+ name = name.strip()
+ time_scope = (va(cp, (45, 222)), va(cp, (168, 255)))
+ time_hsv = cropimg(self.recog.img, time_scope)
+ time_hsv = cv2.cvtColor(time_hsv, cv2.COLOR_RGB2HSV)
+ if 165 < time_hsv[0][0][0] < 175:
+ time_img = thres2(cropimg(self.recog.gray, time_scope), 180)
+ time_img = cv2.copyMakeBorder(
+ time_img, 48, 48, 48, 48, cv2.BORDER_REPLICATE
+ )
+ time = rapidocr.engine(
+ time_img,
+ use_det=True,
+ use_cls=False,
+ use_rec=True,
+ )[0][0][1]
+ if time:
+ time = time.strip()
+ else:
+ time = None
+ clue_list.append(
+ {"name": name, "time": time, "scope": tl2p(cp)}
+ )
+ else:
+ break
+ if clue_list:
+ list_name = "接收库" if receive else "自有库"
+ logger.info(f"{cl}号线索{list_name}:{clue_list}")
+ selected = None
+ for c in clue_list:
+ if c["time"]:
+ selected = c
+ break
+ selected = selected or clue_list[0]
+ self.tap(selected["scope"])
+ if clue_status[cl] == "available":
+ clue_status[cl] = "friend"
+ elif clue_status[cl] == "available_self_only":
+ clue_status[cl] = "self_only"
+ elif clue_status[cl] == "self":
+ clue_status[cl] = "friend"
+ else:
+ clue_status[cl] = None
+ else:
+ if clue_status[cl] == "available":
+ clue_status[cl] = "available_self_only"
+ elif clue_status[cl] == "available_self_only":
+ clue_status[cl] = None
+ elif clue_status[cl] == "self":
+ clue_status[cl] = "self_only"
+ else:
+ clue_status[cl] = None
+
+ elif scene == Scene.CLUE_GIVE_AWAY:
+ give_away_true = self.leifeng_mode or (
+ not self.leifeng_mode and self.clue_count > self.clue_count_limit
+ )
+ if (c := clue_cls("give_away")) and give_away_true:
+ if not friend_clue:
+ if self.find(
+ "clue/icon_notification", scope=((1400, 0), (1920, 400))
+ ):
+ self.sleep()
+ continue
+ for i in range(4):
+ label_scope = ((1450, 228 + i * 222), (1580, 278 + i * 222))
+ if not self.find("clue/label_give_away", scope=label_scope):
+ break
+ name_top_left = (870, 127 + 222 * i)
+ name_scope = (name_top_left, va(name_top_left, (383, 62)))
+ name = rapidocr.engine(
+ cropimg(self.recog.gray, name_scope),
+ use_det=True,
+ use_cls=False,
+ use_rec=True,
+ )[0][0][1]
+ if name:
+ name = name.strip()
+ data = {"name": name}
+ for j in range(1, 8):
+ pos = (1230 + j * 64, 142 + i * 222)
+ data[j] = self.get_color(pos)[0] < 137
+ friend_clue.append(data)
+ logger.debug(friend_clue)
+ friend = None
+ for idx, fc in enumerate(friend_clue):
+ if not fc[c]:
+ friend = idx
+ fc[c] = True
+ break
+ friend = friend or 0
+ logger.info(f"给{friend_clue[friend]['name']}送一张线索{c}")
+ self.tap(clue_scope["give_away"])
+ self.clue_count -= 1
+ self.tap((1790, 200 + friend * 222))
+ else:
+ ctm.complete("give_away")
+ self.tap((1868, 54))
- def enter_room(self, room: str) -> tp.Rectangle:
- """ 获取房间的位置并进入 """
+ elif scene == Scene.CLUE_SUMMARY:
+ self.back()
- # 获取基建各个房间的位置
- base_room = segment.base(self.recog.img, self.find('control_central', strict=True))
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
- # 将画面外的部分删去
- _room = base_room[room]
- for i in range(4):
- _room[i, 0] = max(_room[i, 0], 0)
- _room[i, 0] = min(_room[i, 0], self.recog.w)
- _room[i, 1] = max(_room[i, 1], 0)
- _room[i, 1] = min(_room[i, 1], self.recog.h)
+ else:
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
+ self.enter_room("meeting")
- # 点击进入
- self.tap(_room[0], interval=3)
- while self.find('control_central') is not None:
- self.tap(_room[0], interval=3)
+ shop_solver = CreditShop(self.device, self.recog)
+ shop_solver.run()
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
- def drone(self, room: str, one_time=False, not_return=False):
- logger.info('基建:无人机加速')
+ def adjust_order_time(self, accelerate, room):
+ error_count = 0
+ action_required_task = scheduling(self.tasks)
+ while (
+ action_required_task is not None and action_required_task.meta_data == room
+ ):
+ self.tap(accelerate)
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ self.tap((self.recog.w * 1320 // 1920, self.recog.h * 502 // 1080))
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ self.tap((self.recog.w * 3 // 4, self.recog.h * 4 // 5))
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ while self.find("bill_accelerate") is None:
+ if error_count > 5:
+ raise Exception("未成功进入订单界面")
+ self.tap((self.recog.w // 20, self.recog.h * 19 // 20), interval=1)
+ error_count += 1
+ _time = self.double_read_time(
+ (
+ (self.recog.w * 650 // 2496, self.recog.h * 660 // 1404),
+ (self.recog.w * 815 // 2496, self.recog.h * 710 // 1404),
+ ),
+ use_digit_reader=True,
+ )
+ task_time = _time - timedelta(minutes=config.conf.run_order_delay)
+ if task := find_next_task(
+ self.tasks, task_type=TaskTypes.RUN_ORDER, meta_data=room
+ ):
+ task.time = task_time
+ logger.info(
+ f'房间 {room} 无人机加速后接单时间为 {task_time.strftime("%H:%M:%S")}'
+ )
+ action_required_task = scheduling(self.tasks)
+ else:
+ break
+ def drone(
+ self,
+ room: str,
+ not_customize=False,
+ not_return=False,
+ adjust_time=False,
+ skip_enter=False,
+ ):
+ logger.info("基建:无人机加速" if not adjust_time else "开始调整订单时间")
+ all_in = 0
+ if not not_customize:
+ all_in = len(self.op_data.run_order_rooms)
# 点击进入该房间
- self.enter_room(room)
+ if not skip_enter:
+ self.enter_room(room)
# 进入房间详情
self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3)
# 关闭掉房间总览
error_count = 0
- while self.find('factory_accelerate') is None and self.find('bill_accelerate') is None:
+ while (
+ self.find("factory_accelerate") is None
+ and self.find("bill_accelerate") is None
+ ):
if error_count > 5:
- raise Exception('未成功进入无人机界面')
+ raise Exception("未成功进入无人机界面")
self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3)
error_count += 1
- accelerate = self.find('factory_accelerate')
+ accelerate = self.find("factory_accelerate")
if accelerate:
- logger.info('制造站加速')
+ drone_count = self.digit_reader.get_drone(self.recog.gray)
+ logger.info(f"当前无人机数量为:{drone_count}")
+ if drone_count < config.conf.drone_count_limit or drone_count > 200:
+ logger.info(f"无人机数量小于{config.conf.drone_count_limit}->停止")
+ return
+ logger.info("制造站加速")
self.tap(accelerate)
- self.tap_element('all_in')
+ # self.tap_element('all_in')
+ # 如果不是全部all in
+ if all_in > 0:
+ tap_times = (
+ drone_count - config.conf.drone_count_limit
+ ) # 修改为无人机阈值
+ for _count in range(tap_times):
+ self.tap((self.recog.w * 0.7, self.recog.h * 0.5), interval=0.1)
+ else:
+ self.tap_element("all_in")
self.tap(accelerate, y_rate=1)
-
else:
- accelerate = self.find('bill_accelerate')
- while accelerate:
- logger.info('贸易站加速')
+ accelerate = self.find("bill_accelerate")
+ while accelerate and not adjust_time:
+ logger.info("贸易站加速")
self.tap(accelerate)
- self.tap_element('all_in')
+ self.tap_element("all_in")
self.tap((self.recog.w * 0.75, self.recog.h * 0.8))
- while self.get_infra_scene() == Scene.CONNECTING:
- self.sleep(3)
- if one_time:
- drone_count = self.read_screen(self.recog.img, type='drone_mood', cord=(
- int(self.recog.w * 1150 / 1920), int(self.recog.h * 35 / 1080), int(self.recog.w * 1295 / 1920),
- int(self.recog.h * 72 / 1080)), limit=200)
- logger.info(f'当前无人机数量为:{drone_count}')
- self.recog.update()
- self.recog.save_screencap('run_order')
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ self.recog.update()
+ if not (
+ self.drone_room is None
+ or (
+ self.drone_room == room and room in self.op_data.run_order_rooms
+ )
+ ):
+ break
+ if not_customize:
+ drone_count = self.digit_reader.get_drone(self.recog.gray)
+ logger.info(f"当前无人机数量为:{drone_count}")
# 200 为识别错误
- if drone_count < 100 or drone_count ==200:
- logger.info(f"无人机数量小于92->停止")
+ if (
+ drone_count < config.conf.drone_count_limit
+ or drone_count == 201
+ ):
+ logger.info(
+ f"无人机数量小于{config.conf.drone_count_limit}->停止"
+ )
break
st = accelerate[1] # 起点
ed = accelerate[0] # 终点
# 0.95, 1.05 are offset compensations
- self.swipe_noinertia(st, (ed[0] * 0.95 - st[0] * 1.05, 0), rebuild=True)
- accelerate = self.find('bill_accelerate')
- if not_return: return
- logger.info('返回基建主界面')
- self.back(interval=2, rebuild=False)
- self.back(interval=2)
-
- def get_arrange_order(self) -> ArrangeOrder:
- best_score, best_order = 0, None
- for order in ArrangeOrder:
- score = self.recog.score(arrange_order_res[order][0])
- if score is not None and score[0] > best_score:
- best_score, best_order = score[0], order
- # if best_score < 0.6:
- # raise RecognizeError
- logger.debug((best_score, best_order))
- return best_order
-
- def switch_arrange_order(self, index: int, asc="false") -> None:
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder(index)][0],
- self.recog.h * arrange_order_res[ArrangeOrder(index)][1]), interval=0, rebuild=False)
- # 点个不需要的
- if index < 4:
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder(index + 1)][0],
- self.recog.h * arrange_order_res[ArrangeOrder(index)][1]), interval=0, rebuild=False)
- else:
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder(index - 1)][0],
- self.recog.h * arrange_order_res[ArrangeOrder(index)][1]), interval=0, rebuild=False)
- # 切回来
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder(index)][0],
- self.recog.h * arrange_order_res[ArrangeOrder(index)][1]), interval=0.2, rebuild=True)
- # 倒序
- if asc != "false":
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder(index)][0],
- self.recog.h * arrange_order_res[ArrangeOrder(index)][1]), interval=0.2, rebuild=True)
-
- def scan_agant(self, agent: list[str], error_count=0, max_agent_count=-1):
- try:
- # 识别干员
- self.recog.update()
- ret = character_recognize.agent(self.recog.img) # 返回的顺序是从左往右从上往下
- # 提取识别出来的干员的名字
- agent_name = set([x[0] for x in ret])
- agent_size = len(agent)
- select_name = []
- for y in ret:
- name = y[0]
- if name in agent:
- select_name.append(name)
- # self.get_agent_detail((y[1][0]))
- self.tap((y[1][0]))
- agent.remove(name)
- # 如果是按照个数选择 Free
- if max_agent_count != -1:
- if len(select_name) >= max_agent_count:
- return select_name, ret
- return select_name, ret
- except Exception as e:
- error_count += 1
- if error_count < 3:
- logger.exception(e)
- self.sleep(3)
- return self.scan_agant(agent, error_count, max_agent_count)
- else:
- raise e
+ self.swipe_noinertia(st, (ed[0] * 0.95 - st[0] * 1.05, 0))
+ accelerate = self.find("bill_accelerate")
+ if adjust_time:
+ self.adjust_order_time(accelerate, room)
+ if not_return:
+ return
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
+
+ # 用于制造站切换产物,请注意在调用该函数前有足够的无人机,并补足相应制造站产物,目前仅支持中级作战记录与赤金之间的切换
+ # def 制造站切换产物(self, room: str, 目标产物: str, not_customize=False, not_return=False):
+ # # 点击进入该房间
+ # self.enter_room(room)
+ # while self.get_infra_scene() == 9:
+ # time.sleep(1)
+ # self.recog.update()
+ # # 进入房间详情
+ # self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3)
+ # # 关闭掉房间总览
+ # error_count = 0
+ # while self.find('factory_accelerate') is None:
+ # if error_count > 5:
+ # raise Exception('未成功进入制造详情界面')
+ # self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=3)
+ # error_count += 1
+ # accelerate = self.find('factory_accelerate')
+ # 无人机数量 = self.digit_reader.get_drone(self.recog.gray, self.recog.h, self.recog.w)
+ # if accelerate:
+ # self.tap_element('factory_accelerate')
+ # self.recog.update()
+ # 剩余制造加速总时间 = self.digit_reader.识别制造加速总剩余时间(self.recog.gray, self.recog.h, self.recog.w)
+ # # logger.info(f'制造站 B{room[5]}0{room[7]} 剩余制造总时间为 {剩余制造加速总时间}')
+ # 时 = int(剩余制造加速总时间[0:3])
+ # if 时 > 118: 当前产物 = '经验'
+ # else: 当前产物 = '赤金'
+ # if 当前产物 == 目标产物:
+ # logger.info('返回基建主界面')
+ # while self.get_infra_scene() != 201:
+ # if self.find('index_infrastructure') is not None:
+ # self.tap_element('index_infrastructure')
+ # elif self.find('12cadpa') is not None:
+ # self.device.tap((self.recog.w // 2, self.recog.h // 2))
+ # else:
+ # self.back()
+ # self.recog.update()
+ # else:
+ # logger.info(f'制造站 B{room[5]}0{room[7]} 当前产物为{当前产物},切换产物为{目标产物}')
+ # 需要无人机数 = 0
+ # while 需要无人机数 < 10:
+ # 总分钟数 = int(剩余制造加速总时间[4:6]) + 60 * 时
+ # if 当前产物 == '赤金':
+ # 需要无人机数 = (总分钟数 % 72) // 3 + 1
+ # elif 当前产物 == '经验':
+ # 需要无人机数 = (总分钟数 % 180) // 3 + 1
+ # else:
+ # logger.warning('目前不支持该产物切换策略,尚待完善')
+ # logger.info('返回基建主界面')
+ # while self.get_infra_scene() != 201:
+ # if self.find('index_infrastructure') is not None:
+ # self.tap_element('index_infrastructure')
+ # elif self.find('12cadpa') is not None:
+ # self.device.tap((self.recog.w // 2, self.recog.h // 2))
+ # else:
+ # self.back()
+ # self.recog.update()
+ # if 需要无人机数 > 无人机数量 - 10:
+ # logger.warning(f'''
+ # 切换产物需要无人机{需要无人机数}个,当前仅有{无人机数量}个,
+ # 无法切换产物,建议该任务至少在{(需要无人机数 - 无人机数量 + 10) * 3.5 // 3}分钟后再执行
+ # ''')
+ # logger.info('返回基建主界面')
+ # while self.get_infra_scene() != 201:
+ # if self.find('index_infrastructure') is not None:
+ # self.tap_element('index_infrastructure')
+ # elif self.find('12cadpa') is not None:
+ # self.device.tap((self.recog.w // 2, self.recog.h // 2))
+ # else:
+ # self.back()
+ # self.recog.update()
+ # else:
+ # logger.warning(f'需要加无人机{需要无人机数}个')
+ # for 次数 in range(需要无人机数):
+ # self.tap((self.recog.w * 1320 // 1920, self.recog.h * 502 // 1080), interval=0.05)
+ # self.recog.update()
+ # 剩余制造加速总时间 = self.digit_reader.识别制造加速总剩余时间(
+ # self.recog.gray, self.recog.h, self.recog.w)
+ # # logger.info(f'制造站 B{room[5]}0{room[7]} 剩余制造总时间为 {剩余制造加速总时间}')
+ # 总分钟数 = int(剩余制造加速总时间[4:6]) + 60 * 时
+ # if 当前产物 == '赤金':
+ # 需要无人机数 = (总分钟数 % 72) // 3 + 1
+ # elif 当前产物 == '经验':
+ # 需要无人机数 = (总分钟数 % 180) // 3 + 1
+ # else:
+ # logger.warning('目前不支持该产物切换策略,尚待完善')
+ # logger.info('返回基建主界面')
+ # while self.get_infra_scene() != 201:
+ # if self.find('index_infrastructure') is not None:
+ # self.tap_element('index_infrastructure')
+ # elif self.find('12cadpa') is not None:
+ # self.device.tap((self.recog.w // 2, self.recog.h // 2))
+ # else:
+ # self.back()
+ # self.recog.update()
+ # self.tap((self.recog.w * 3 // 4, self.recog.h * 4 // 5), interval=3) # 确认加速
+ # self.tap((self.recog.w * 9 // 10, self.recog.h // 2), interval=1) # 点击当前产品
+ # if 目标产物 == '经验':
+ # self.tap((self.recog.w // 2, self.recog.h // 2), interval=1) # 点击中级作战记录
+ # elif 目标产物 == '赤金':
+ # self.tap((self.recog.w // 10, self.recog.h // 3), interval=1) # 进入贵金属分类
+ # self.tap((self.recog.w // 2, self.recog.h // 4), interval=1) # 点击赤金
+ # self.tap((self.recog.w * 3 // 4, self.recog.h * 2 // 7), interval=1) # 点击最多
+ # self.tap((self.recog.w * 3 // 4, self.recog.h * 5 // 6), interval=1) # 确认数量
+ # self.tap((self.recog.w * 3 // 4, self.recog.h * 7 // 10), interval=1) # 确认更改
def get_order(self, name):
- if (name in self.agent_base_config.keys()):
- if "ArrangeOrder" in self.agent_base_config[name].keys():
- return True, self.agent_base_config[name]["ArrangeOrder"]
- else:
- return False, self.agent_base_config["Default"]["ArrangeOrder"]
- return False, self.agent_base_config["Default"]["ArrangeOrder"]
-
- def detail_filter(self, turn_on, type="not_in_dorm"):
- logger.info(f'开始 {("打开" if turn_on else "关闭")} {type} 筛选')
- self.tap((self.recog.w * 0.95, self.recog.h * 0.05), interval=1)
- if type == "not_in_dorm":
- not_in_dorm = self.find('not_in_dorm')
- if turn_on ^ (not_in_dorm is not None):
- self.tap((self.recog.w * 0.3, self.recog.h * 0.5), interval=0.5)
- # 确认
- self.tap((self.recog.w * 0.8, self.recog.h * 0.8), interval=0.5)
-
- def choose_agent(self, agents: list[str], room: str) -> None:
+ if name in self.op_data.operators:
+ return True, self.op_data.operators[name].arrange_order
+ else:
+ return False, [2, "false"]
+
+ def tap_confirm(self, room, new_plan=None):
+ if new_plan is None:
+ new_plan = {}
+ self.recog.update()
+ if (
+ room in self.op_data.run_order_rooms
+ and len(new_plan) == 1
+ and config.conf.run_order_buffer_time > 0
+ ):
+ wait_confirm = round(
+ (
+ (self.task.time - datetime.now()).total_seconds()
+ + config.conf.run_order_delay * 60
+ - config.conf.run_order_buffer_time
+ ),
+ 1,
+ )
+ if wait_confirm > 0:
+ logger.info(f"等待跑单 {str(wait_confirm)} 秒")
+ self.sleep(wait_confirm)
+ self.tap_element("confirm_blue")
+ if self.find("arrange_confirm"):
+ _x0 = self.recog.w // 3 * 2 # double confirm
+ _y0 = self.recog.h - 10
+ self.tap((_x0, _y0))
+
+ def choose_train_agent(
+ self, current_room, agents, idx, error_count=0, fast_mode=False
+ ):
+ if current_room[idx] != agents[idx]:
+ while (
+ self.find("arrange_order_options") is None
+ and self.find("confirm_blue") is None
+ ):
+ if error_count > 3:
+ raise Exception("未成功进入干员选择界面")
+ self.ctap((self.recog.w * 0.82, self.recog.h * 0.18 * (idx + 1)))
+ error_count += 1
+ self.choose_agent([agents[idx]], "train", fast_mode)
+ self.tap_confirm("train")
+
+ def choose_train(self, agents: list[str], fast_mode=True):
+ current_room = self.op_data.get_current_room("train", True)
+ self.choose_train_agent(current_room, agents, 0, 0, fast_mode)
+ # 训练室第二个人的干员识别会出错(工作中的干员无法识别 + 正在训练的干员无法换下)
+ # self.choose_train_agent(current_room, agents, 1, 0, fast_mode)
+
+ def choose_agent(
+ self, agents: list[str], room: str, fast_mode=True, train_index=0
+ ) -> None:
"""
:param order: ArrangeOrder, 选择干员时右上角的排序功能
"""
- first_name = ''
+ first_name = ""
max_swipe = 50
+ position = [
+ (0.35, 0.35),
+ (0.35, 0.75),
+ (0.45, 0.35),
+ (0.45, 0.75),
+ (0.55, 0.35),
+ ]
+ # 空位置跳过安排
+ if "" in agents:
+ fast_mode = False
+ agents = [item for item in agents if item != ""]
for idx, n in enumerate(agents):
- if n == '':
- agents[idx] = 'Free'
+ if room.startswith("dorm") and n in self.op_data.operators.keys():
+ __agent = self.op_data.operators[n]
+ if __agent.mood == __agent.upper_limit and not __agent.room.startswith(
+ "dorm"
+ ):
+ agents[idx] = "Free"
+ logger.info("检测满心情释放休息位")
+ elif agents[idx] == "Free" and self.task.type != TaskTypes.RE_ORDER:
+ if self.op_data.config.free_room:
+ current_free = self.op_data.get_current_operator(room, idx)
+ if (
+ current_free
+ and current_free.mood < current_free.upper_limit
+ ):
+ agents[idx] = current_free.name
agent = copy.deepcopy(agents)
- logger.info(f'安排干员 :{agent}')
+ exists = []
+ if fast_mode:
+ current_room = self.op_data.get_current_room(room, True)
+ # 如果空位置进房间会被向前挤
+ current_room = sorted(current_room, key=lambda x: x == "")
+ differences = []
+ for i in range(len(current_room)):
+ if current_room[i] not in agents:
+ differences.append(i)
+ else:
+ exists.append(current_room[i])
+ if room == "train":
+ differences = [x for x in differences if x == train_index]
+ for pos in differences:
+ if current_room[pos] != "":
+ self.tap(
+ (
+ self.recog.w * position[pos][0],
+ self.recog.h * position[pos][1],
+ ),
+ interval=0,
+ )
+ agent = [x for x in agents if x not in exists]
+ logger.info(f"安排干员 :{agent}")
# 若不是空房间,则清空工作中的干员
is_dorm = room.startswith("dorm")
- h, w = self.recog.h, self.recog.w
first_time = True
# 在 agent 中 'Free' 表示任意空闲干员
- free_num = agent.count('Free')
+ free_num = agent.count("Free")
for i in range(agent.count("Free")):
agent.remove("Free")
index_change = False
pre_order = [2, False]
right_swipe = 0
retry_count = 0
- # 如果重复进入宿舍则需要排序
selected = []
- logger.info(f'上次进入房间为:{self.last_room},本次房间为:{room}')
- if self.last_room.startswith('dorm') and is_dorm:
- self.detail_filter(False)
+ logger.info(f"上次进入房间为:{self.last_room},本次房间为:{room}")
+ self.detail_filter()
+ if self.detect_arrange_order()[0] == "信赖值":
+ self.switch_arrange_order("工作状态")
+ siege = False # 推进之王
+ last_special_filter = ""
while len(agent) > 0:
- if retry_count > 3: raise Exception(f"到达最大尝试次数 3次")
+ if retry_count > 1:
+ raise Exception("到达最大尝试次数 1次")
if right_swipe > max_swipe:
# 到底了则返回再来一次
- for _ in range(right_swipe):
- self.swipe_only((w // 2, h // 2), (w // 2, 0), interval=0.5)
- right_swipe = 0
+ right_swipe = self.swipe_left(right_swipe)
max_swipe = 50
retry_count += 1
- self.detail_filter(False)
+ self.detail_filter()
if first_time:
# 清空
if is_dorm:
self.switch_arrange_order(3, "true")
- pre_order = [3, 'true']
- self.tap((self.recog.w * 0.38, self.recog.h * 0.95), interval=0.5)
- changed, ret = self.scan_agant(agent)
+ pre_order = [3, "true"]
+ if not fast_mode:
+ self.tap((self.recog.w * 0.38, self.recog.h * 0.95), interval=0.5)
+ changed, ret = self.scan_agent(agent)
if changed:
selected.extend(changed)
- if len(agent) == 0: break
+ if len(agent) == 0:
+ break
index_change = True
# 如果选中了人,则可能需要重新排序
if index_change or first_time:
# 第一次则调整
is_custom, arrange_type = self.get_order(agent[0])
- if is_dorm and not (agent[0] in self.operators.keys() and
- 'room' in self.operators[agent[0]].keys() and self.operators[agent[0]]['room'].startswith(
- 'dormitory')):
- arrange_type = (3, 'true')
+ if is_dorm and not (
+ agent[0] in self.op_data.operators.keys()
+ and self.op_data.operators[agent[0]].room.startswith("dormitory")
+ ):
+ arrange_type = (3, "true")
# 如果重新排序则滑到最左边
if pre_order[0] != arrange_type[0] or pre_order[1] != arrange_type[1]:
self.switch_arrange_order(arrange_type[0], arrange_type[1])
# 滑倒最左边
- self.sleep(interval=0.5, rebuild=True)
- right_swipe = self.swipe_left(right_swipe, w, h)
+ self.sleep(interval=0.5)
+ if not siege:
+ right_swipe = self.swipe_left(right_swipe)
pre_order = arrange_type
first_time = False
- changed, ret = self.scan_agant(agent)
+ if (
+ not siege
+ and not is_dorm
+ and agent
+ and all(
+ element in ["推进之王", "安哲拉", "斯卡蒂", "幽灵鲨", "乌尔比安"]
+ for element in agent
+ )
+ ):
+ siege = True
+
+ if agent[0] in ["推进之王", "安哲拉", "斯卡蒂", "幽灵鲨"]:
+ self.detail_filter(恢复类后勤=True)
+ if last_special_filter != "恢复类后勤":
+ right_swipe = 0
+ last_special_filter = "恢复类后勤"
+ else:
+ self.detail_filter(功能类后勤=True)
+ if last_special_filter != "功能类后勤":
+ right_swipe = 0
+ last_special_filter = "功能类后勤"
+ self.switch_arrange_order(3, "true")
+ changed, ret = self.scan_agent(agent)
if changed:
selected.extend(changed)
# 如果找到了
index_change = True
+ siege = False
else:
# 如果没找到 而且右移次数大于5
if ret[0][0] == first_name and right_swipe > 5:
@@ -1271,11 +2491,15 @@ def choose_agent(self, agents: list[str], room: str) -> None:
else:
first_name = ret[0][0]
index_change = False
- st = ret[-2][1][2] # 起点
- ed = ret[0][1][1] # 终点
+ st = ret[-2][1][0] # 起点
+ ed = ret[0][1][0] # 终点
self.swipe_noinertia(st, (ed[0] - st[0], 0))
right_swipe += 1
- if len(agent) == 0: break;
+ if len(agent) == 0:
+ if siege:
+ self.detail_filter()
+ right_swipe = 0
+ break
# 安排空闲干员
if free_num:
@@ -1283,409 +2507,962 @@ def choose_agent(self, agents: list[str], room: str) -> None:
self.tap((self.recog.w * 0.38, self.recog.h * 0.95), interval=0.5)
if not first_time:
# 滑动到最左边
- self.sleep(interval=0.5, rebuild=False)
- right_swipe = self.swipe_left(right_swipe, w, h)
- self.detail_filter(True)
+ self.sleep(interval=0.5)
+ right_swipe = self.swipe_left(right_swipe)
+ self.detail_filter(未进驻=True)
self.switch_arrange_order(3, "true")
# 只选择在列表里面的
# 替换组小于20才休息,防止进入就满心情进行网络连接
- free_list = [v["name"] for k, v in self.operators.items() if
- v["name"] not in agents and v["type"] != 'high']
- free_list.extend([_name for _name in agent_list if _name not in self.operators.keys()])
- free_list = list(set(free_list) - set(self.free_blacklist))
+ free_list = [
+ v.name
+ for k, v in self.op_data.operators.items()
+ if v.name not in agents
+ and v.operator_type != "high"
+ and v.current_room == ""
+ ]
+ free_list.extend(
+ [
+ _name
+ for _name in agent_list
+ if _name not in self.op_data.operators.keys()
+ and _name not in agents
+ ]
+ )
+ train_support = self.op_data.get_train_support()
+ free_list = list(set(free_list) - set(self.op_data.config.free_blacklist))
+ if train_support in free_list:
+ free_list.remove(train_support)
while free_num:
- selected_name, ret = self.scan_agant(free_list, max_agent_count=free_num)
+ selected_name, ret = self.scan_agent(
+ free_list, max_agent_count=free_num
+ )
selected.extend(selected_name)
free_num -= len(selected_name)
while len(selected_name) > 0:
- agents[agents.index('Free')] = selected_name[0]
+ agents[agents.index("Free")] = selected_name[0]
selected_name.remove(selected_name[0])
if free_num == 0:
break
else:
- st = ret[-2][1][2] # 起点
- ed = ret[0][1][1] # 终点
+ st = ret[-2][1][0] # 起点
+ ed = ret[0][1][0] # 终点
self.swipe_noinertia(st, (ed[0] - st[0], 0))
right_swipe += 1
# 排序
if len(agents) != 1:
# 左移
- self.swipe_left(right_swipe, w, h)
- self.tap((self.recog.w * arrange_order_res[ArrangeOrder.SKILL][0],
- self.recog.h * arrange_order_res[ArrangeOrder.SKILL][1]), interval=0.5, rebuild=False)
- position = [(0.35, 0.35), (0.35, 0.75), (0.45, 0.35), (0.45, 0.75), (0.55, 0.35)]
+ right_swipe = self.swipe_left(right_swipe)
+ self.switch_arrange_order("技能")
not_match = False
+ exists.extend(selected)
for idx, item in enumerate(agents):
- if agents[idx] != selected[idx] or not_match:
+ if agents[idx] != exists[idx] or not_match:
not_match = True
- p_idx = selected.index(agents[idx])
- self.tap((self.recog.w * position[p_idx][0], self.recog.h * position[p_idx][1]), interval=0,
- rebuild=False)
- self.tap((self.recog.w * position[p_idx][0], self.recog.h * position[p_idx][1]), interval=0,
- rebuild=False)
+ p_idx = exists.index(agents[idx])
+ self.tap(
+ (
+ self.recog.w * position[p_idx][0],
+ self.recog.h * position[p_idx][1],
+ ),
+ interval=0,
+ )
+ self.tap(
+ (
+ self.recog.w * position[p_idx][0],
+ self.recog.h * position[p_idx][1],
+ ),
+ interval=0,
+ )
+ logger.debug("验证干员选择..")
+ self.swipe_left(right_swipe)
+ self.switch_arrange_order(2)
+ if not self.verify_agent(agents):
+ raise Exception("检测到干员选择错误,重新选择")
self.last_room = room
- logger.info(f"设置上次房间为{self.last_room}")
-
- def swipe_left(self, right_swipe, w, h):
- for _ in range(right_swipe):
- self.swipe_only((w // 2, h // 2), (w // 2, 0), interval=0.5)
- return 0
- def get_agent_from_room(self, room, read_time_index=[]):
- error_count = 0
- if room == 'meeting':
- time.sleep(3)
- while self.find('room_detail') is None:
- if error_count > 3:
- raise Exception('未成功进入房间')
- self.tap((self.recog.w * 0.05, self.recog.h * 0.4), interval=0.5)
- error_count += 1
- length = len(self.currentPlan[room])
- if length > 3: self.swipe((self.recog.w * 0.8, self.recog.h * 0.8), (0, self.recog.h * 0.4), interval=1,
- rebuild=True)
- name_p = [((1460, 155), (1700, 210)), ((1460, 370), (1700, 420)), ((1460, 585), (1700, 630)),
- ((1460, 560), (1700, 610)), ((1460, 775), (1700, 820))]
- time_p = [((1650, 270, 1780, 305)), ((1650, 480, 1780, 515)), ((1650, 690, 1780, 725)),
- ((1650, 665, 1780, 700)), ((1650, 875, 1780, 910))]
- mood_p = [((1685, 213, 1780, 256)), ((1685, 422, 1780, 465)), ((1685, 632, 1780, 675)),
- ((1685, 612, 1780, 655)), ((1685, 822, 1780, 865))]
+ def reset_room_time(self, room):
+ for _operator in self.op_data.operators.keys():
+ if self.op_data.operators[_operator].room == room:
+ self.op_data.operators[_operator].time_stamp = None
+
+ def turn_on_room_detail(self, room):
+ for enter_times in range(3):
+ for retry_times in range(10):
+ if pos := self.find("room_detail"):
+ if all(self.get_color((1233, 1)) > [252] * 3):
+ return
+ logger.info("等待动画")
+ self.sleep(interval=0.5)
+ elif pos := self.find("arrange_check_in"):
+ self.tap(pos, interval=0.7)
+ else:
+ self.sleep()
+ for back_time in range(3):
+ if pos := self.find("control_central"):
+ break
+ self.back()
+ if not pos:
+ self.back_to_infrastructure()
+ self.enter_room(room)
+ self.reset_room_time(room)
+ raise Exception("未成功进入房间")
+
+ def get_agent_from_room(self, room, read_time_index=None):
+ if read_time_index is None:
+ read_time_index = []
+ if room == "meeting" and not self.leifeng_mode:
+ self.sleep(0.5)
+ self.recog.update()
+ clue_res = self.read_screen(
+ self.recog.img, limit=10, cord=((645, 977), (755, 1018))
+ )
+ if clue_res != 11:
+ self.clue_count = clue_res
+ logger.info(f"当前拥有线索数量为{self.clue_count}")
+ self.turn_on_room_detail(room)
+ # 如果是宿舍则全读取
+ if room.startswith("dorm"):
+ read_time_index = [
+ i
+ for i, obj in enumerate(self.op_data.plan[room])
+ if obj.agent == "Free" or obj.agent == "菲亚梅塔"
+ ]
+ while self.detect_product_complete():
+ logger.info("检测到产物收取提示")
+ self.sleep(1)
+ length = len(self.op_data.plan[room])
+ if length > 3:
+ while self.get_color((1800, 138))[0] > 51:
+ self.swipe(
+ (self.recog.w * 0.8, self.recog.h * 0.5),
+ (0, self.recog.h * 0.45),
+ duration=500,
+ interval=1,
+ )
+ name_x = (1288, 1869)
+ name_y = [(135, 326), (344, 535), (553, 744), (532, 723), (741, 932)]
+ name_p = [tuple(zip(name_x, y)) for y in name_y]
+ time_x = (1650, 1780)
+ time_y = [(270, 305), (480, 515), (690, 725), (668, 703), (877, 912)]
+ time_p = [tuple(zip(time_x, y)) for y in time_y]
+ mood_x = (1470, 1780)
+ mood_y = [(219, 220), (428, 429), (637, 638), (615, 616), (823, 825)]
+ mood_y = (219, 428, 637, 615, 823)
+ mood_y = [(y, y + 1) for y in mood_y]
+ mood_p = [tuple(zip(mood_x, y)) for y in mood_y]
result = []
swiped = False
for i in range(0, length):
if i >= 3 and not swiped:
- self.swipe((self.recog.w * 0.8, self.recog.h * 0.8), (0, -self.recog.h * 0.4), interval=1, rebuild=True)
+ while self.get_color((1800, 930))[0] > 51:
+ self.swipe(
+ (self.recog.w * 0.8, self.recog.h * 0.5),
+ (0, -self.recog.h * 0.45),
+ duration=500,
+ interval=1,
+ )
swiped = True
data = {}
- data['agent'] = character_recognize.agent_name(
- self.recog.img[name_p[i][0][1]:name_p[i][1][1], name_p[i][0][0]:name_p[i][1][0]], self.recog.h*1.1)
- error_count = 0
- while i>=3 and data['agent'] !='' and (next((e for e in result if e['agent'] == data['agent']), None)) is not None:
- logger.warning("检测到滑动可能失败")
- self.swipe((self.recog.w * 0.8, self.recog.h * 0.8), (0, -self.recog.h * 0.4), interval=1, rebuild=True)
- data['agent'] = character_recognize.agent_name(
- self.recog.img[name_p[i][0][1]:name_p[i][1][1], name_p[i][0][0]:name_p[i][1][0]], self.recog.h*1.1)
- error_count+=1
- if error_count>4:
- raise Exception("超过出错上限")
- data['mood'] = self.read_screen(self.recog.img, cord=mood_p[i], change_color=True)
- if data['agent'] not in self.operators.keys():
- self.operators[data['agent']] = {"type": "low", "name": data['agent'], "group": '', 'current_room': '',
- 'resting_priority': 'low', "index": -1, 'mood': data['mood'],
- "upper_limit": 24}
+ if self.find("infra_no_operator", scope=name_p[i]):
+ _name = ""
+ else:
+ _name = self.read_screen(
+ cropimg(self.recog.gray, name_p[i]), type="name"
+ )
+ _mood = 24
+ # 如果房间不为空
+ if _name != "":
+ if _name not in self.op_data.operators.keys() and _name in agent_list:
+ self.op_data.add(Operator(_name, ""))
+ update_time = False
+ agent = self.op_data.operators[_name]
+ if self.op_data.operators[_name].need_to_refresh(r=room) or (
+ self.tasks and self.tasks[0].type == TaskTypes.SHIFT_ON
+ ):
+ _mood = self.read_accurate_mood(cropimg(self.recog.gray, mood_p[i]))
+ update_time = True
+ else:
+ _mood = self.op_data.operators[_name].current_mood()
+ high_no_time = self.op_data.update_detail(
+ _name, _mood, room, i, update_time
+ )
+ data["depletion_rate"] = agent.depletion_rate
+ if high_no_time is not None and high_no_time not in read_time_index:
+ logger.debug(
+ f"检测到高效组休息时间数据不存在:{room},{high_no_time}"
+ )
+ read_time_index.append(high_no_time)
else:
- self.operators[data['agent']]['mood'] = data['mood']
- self.operators[data['agent']]['current_index'] = i
- self.operators[data['agent']]['current_room'] = room
- if i in read_time_index:
- if data['mood'] in [24] or (data['mood'] == 0 and not room.startswith('dorm')):
- data['time'] = datetime.now()
+ _mood = -1
+ data["agent"] = _name
+ data["mood"] = _mood
+ if i in read_time_index and _name != "":
+ if _mood == 24 or room in ["central", "meeting", "factory"]:
+ data["time"] = datetime.now()
else:
- upperLimit = 21600
- if data['agent']in ['菲亚梅塔','刻俄柏']:
- upperLimit = 43200
- data['time'] = self.double_read_time(time_p[i],upperLimit=upperLimit)
+ upperLimit = 43200
+ logger.debug(f"开始记录时间:{room},{i}")
+ data["time"] = self.double_read_time(
+ time_p[i], upperLimit=upperLimit
+ )
+ self.op_data.refresh_dorm_time(room, i, data)
+ logger.debug(f"停止记录时间:{str(data)}")
result.append(data)
- self.scan_time[room] = datetime.now()
- # update current_room
- for item in result:
- operator = item['agent']
- if operator in self.operators.keys():
- self.operators[operator]['current_room'] = room
- for _operator in self.operators.keys():
- if 'current_room' in self.operators[_operator].keys() and self.operators[_operator][
- 'current_room'] == room and _operator not in [res['agent'] for res in result] :
- self.operators[_operator]['current_room'] = ''
- logger.info(f'重设 {_operator} 至空闲')
+ for _operator in self.op_data.operators.keys():
+ if self.op_data.operators[
+ _operator
+ ].current_room == room and _operator not in [
+ res["agent"] for res in result
+ ]:
+ self.op_data.operators[_operator].current_room = ""
+ self.op_data.operators[_operator].current_index = -1
+ if (
+ self.op_data.config.free_room
+ and self.task is not None
+ and self.task.type != TaskTypes.SHIFT_OFF
+ ):
+ release_task = self.find_next_task(
+ task_type=TaskTypes.RELEASE_DORM, meta_data=_operator
+ )
+ if release_task and self.task != release_task:
+ self.tasks.remove(release_task)
+ logger.info(f"重设 {_operator} 至空闲")
return result
- def agent_arrange(self, plan: tp.BasePlan, read_time_room=[]):
- logger.info('基建:排班')
- in_and_out = []
- fia_room = ""
- rooms = list(plan.keys())
- # 优先替换工作站再替换宿舍
- rooms.sort(key=lambda x: x.startswith('dorm'), reverse=False)
- time_result = {}
- for room in rooms:
- finished = False
- choose_error = 0
- while not finished:
- try:
- error_count = 0
+ def refresh_current_room(self, room, current_index=None):
+ _current_room = self.op_data.get_current_room(room, current_index=current_index)
+ if _current_room is None:
+ self.get_agent_from_room(room)
+ _current_room = self.op_data.get_current_room(room, True)
+ return _current_room
+
+ def get_order_remaining_time(self):
+ error_count = 0
+ while (
+ self.find("factory_accelerate") is None
+ and self.find("bill_accelerate") is None
+ ):
+ if error_count > 5:
+ raise Exception("未成功进入无人机界面")
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.5)
+ error_count += 1
+ # 订单剩余时间
+ execute_time = self.double_read_time(
+ (
+ (int(self.recog.w * 650 / 2496), int(self.recog.h * 660 / 1404)),
+ (int(self.recog.w * 815 / 2496), int(self.recog.h * 710 / 1404)),
+ ),
+ use_digit_reader=True,
+ )
+ return round((execute_time - datetime.now()).total_seconds(), 1)
+
+ def current_room_changed(self, instance):
+ if not self.op_data.first_init:
+ logger.info(f"{instance.name} 房间变动")
+ ref_rooms = (
+ instance.refresh_order_room[1]
+ if instance.refresh_order_room[1]
+ else list(self.op_data.run_order_rooms.keys())
+ )
+ for ref_room in ref_rooms:
+ self.refresh_run_order_time(ref_room)
+
+ def refresh_run_order_time(self, room):
+ logger.debug("检测到插拔房间人员变动!")
+ limit = 15
+ if run_order_task := self.find_next_task(
+ datetime.now() + timedelta(minutes=limit),
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data=room,
+ compare_type=">",
+ ):
+ logger.info(f"移除超过{limit}分钟的跑单任务以刷新时间")
+ self.tasks.remove(run_order_task)
+ run_order_task = self.find_next_task(
+ datetime.now() + timedelta(minutes=limit),
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data=room,
+ compare_type="<",
+ )
+ if run_order_task and run_order_task.time > datetime.now():
+ logger.info(f"移除{limit}分钟以内的跑单任务以强X刷新时间")
+ self.tasks.remove(run_order_task)
+ logger.info("新增强X刷新跑单时间任务")
+ task_time = (
+ datetime.now()
+ if len(self.tasks) == 0
+ else self.tasks[0].time - timedelta(seconds=1)
+ )
+ self.tasks.append(
+ (
+ SchedulerTask(
+ time=task_time,
+ task_plan={},
+ task_type=TaskTypes.REFRESH_TIME,
+ meta_data=room,
+ )
+ )
+ )
+
+ def agent_arrange_room(
+ self, new_plan, room, plan, skip_enter=False, get_time=False
+ ):
+ finished = False
+ choose_error = 0
+ checked = False
+ while not finished:
+ try:
+ error_count = 0
+ if not skip_enter:
self.enter_room(room)
- while self.find('room_detail') is None:
- if error_count > 3:
- raise Exception('未成功进入房间')
- self.tap((self.recog.w * 0.05, self.recog.h * 0.4), interval=0.5)
- error_count += 1
- error_count = 0
- update_base = False
- if ('但书' in plan[room] or '龙舌兰' in plan[room]) and not \
- room.startswith('dormitory') and room not in in_and_out:
- in_and_out.append(room)
- update_base = True
- if '菲亚梅塔' in plan[room] and len(plan[room]) == 2:
- fia_room = room
- update_base = True
- # 如果需要更新当前阵容
- self.scan_time[room] = None
- # 是否该干员变动影响其他房间
- update_room = []
- for operator in (plan[room]):
- if operator in self.operators.keys() and self.operators[operator]['current_room'] != '' and \
- self.operators[operator]['current_room'] != room:
- update_room.append(self.operators[operator]['current_room'])
- for __room in update_room:
- self.scan_time[__room] = None
- if update_base or 'Current' in plan[room]:
- self.current_base[room] = self.get_agent_from_room(room)
- # 纠错 因为网络连接导致房间移位
- if 'Current' in plan[room]:
- # replace current
- for current_idx, _name in enumerate(plan[room]):
- if _name == 'Current':
- current_name = self.current_base[room][current_idx]["agent"]
- if current_name in agent_list:
- plan[room][current_idx] = current_name
- else:
- # 如果空房间或者名字错误,则使用default干员
- plan[room][current_idx] = \
- self.currentPlan[room][current_idx]["agent"]
- self.scan_time[room] = None
- while self.find('arrange_order_options') is None:
- if error_count > 3:
- raise Exception('未成功进入干员选择界面')
- self.tap((self.recog.w * 0.82, self.recog.h * 0.2), interval=1)
- error_count += 1
- error_count = 0
- self.choose_agent(plan[room], room)
- self.recog.update()
- self.tap_element('confirm_blue', detected=True, judge=False, interval=3)
- if self.get_infra_scene() == Scene.INFRA_ARRANGE_CONFIRM:
- x0 = self.recog.w // 3 * 2 # double confirm
- y0 = self.recog.h - 10
- self.tap((x0, y0), rebuild=True)
- time_index = []
- # 如果需要读取时间
- if room in read_time_room:
- time_index = [[data["agent"] for data in self.currentPlan[room]].index('Free')]
- current = self.get_agent_from_room(room, time_index)
+ self.turn_on_room_detail(room)
+ error_count = 0
+ if not checked:
+ if any(
+ any(char in item for item in plan[room])
+ for char in ["但书", "龙舌兰", "佩佩"]
+ ) and not room.startswith("dormitory"):
+ new_plan[room] = self.refresh_current_room(room)
+ if "菲亚梅塔" in plan[room] and len(plan[room]) == 2:
+ new_plan[room] = self.refresh_current_room(room)
+ working_room = self.op_data.operators[plan[room][0]].room
+ new_plan[working_room] = self.op_data.get_current_room(
+ working_room, True
+ )
+ if "Current" in plan[room] or "" in plan[room]:
+ self.refresh_current_room(
+ room,
+ [
+ index
+ for index, value in enumerate(plan[room])
+ if value == "Current"
+ ],
+ )
+ _current_room = self.op_data.get_current_room(room, True)
+ for current_idx, _name in enumerate(plan[room]):
+ if _name == "Current":
+ plan[room][current_idx] = (
+ _current_room[current_idx]
+ if _current_room[current_idx] != ""
+ else "Free"
+ )
+ if _name == "":
+ plan[room][current_idx] = "Free"
+ if (
+ room in self.op_data.run_order_rooms
+ and len(new_plan) == 0
+ and self.task.type != TaskTypes.RUN_ORDER
+ ):
+ if plan[room] != self.op_data.get_current_room(room):
+ self.refresh_run_order_time(room)
+ checked = True
+ current_room = self.op_data.get_current_room(room, True)
+ same = len(plan[room]) == len(current_room)
+ if same:
+ for item1, item2 in zip(plan[room], current_room):
+ if item1 != item2:
+ same = False
+ if not same:
+ # choose_error <= 0 选人如果失败则马上重新选过
+ if (
+ len(new_plan) == 1
+ and config.conf.run_order_buffer_time > 0
+ and choose_error <= 0
+ ):
+ remaining_time = self.get_order_remaining_time()
+ if 0 < remaining_time < (config.conf.run_order_delay + 10) * 60:
+ if config.conf.run_order_buffer_time > 0:
+ self.task.time = (
+ datetime.now()
+ + timedelta(seconds=remaining_time)
+ - timedelta(minutes=config.conf.run_order_delay)
+ )
+ logger.info(f"订单倒计时 {remaining_time}秒")
+ self.back()
+ self.turn_on_room_detail(room)
+ else:
+ logger.info("检测到漏单")
+ send_message("检测到漏单!", level="WARNING")
+ self.reset_room_time(room)
+ raise Exception("检测到漏单!")
+ if room == "train":
+ self.choose_train(plan[room], choose_error <= 0)
+ else:
+ while self.find("arrange_order_options", score=0.5) is None:
+ if error_count > 3:
+ raise Exception("未成功进入干员选择界面")
+ self.ctap((self.recog.w * 0.82, self.recog.h * 0.2))
+ error_count += 1
+ self.choose_agent(plan[room], room, choose_error <= 0)
+ self.tap_confirm(room, new_plan)
+ read_time_index = []
+ if get_time:
+ read_time_index = self.op_data.get_refresh_index(
+ room, plan[room]
+ )
+ if len(new_plan) > 1:
+ self.op_data.operators["菲亚梅塔"].time_stamp = None
+ self.op_data.operators[plan[room][0]].time_stamp = None
+ current = self.get_agent_from_room(room, read_time_index)
for idx, name in enumerate(plan[room]):
- if current[idx]['agent'] != name:
- logger.error(f'检测到的干员{current[idx]["agent"]},需要安排的干员{name}')
- raise Exception('检测到安排干员未成功')
- # 如果不匹配,则退出主界面再重新进房间一次
- if room in read_time_room:
- __name = plan[room][[data["agent"] for data in self.currentPlan[room]].index('Free')]
- time_result[room] = current[time_index[0]]['time']
- if not self.operators[__name]['exhaust_require']:
- time_result[room] = time_result[room] - timedelta(seconds=600)
- finished = True
- # back to 基地主界面
- while self.scene() == Scene.CONNECTING:
- self.sleep(3)
- except Exception as e:
- logger.exception(e)
- choose_error += 1
- self.recog.update()
- back_count = 0
- while self.get_infra_scene() != Scene.INFRA_MAIN:
- self.back()
- self.recog.update()
- back_count+=1
- if back_count>3:
- raise e
- if choose_error > 3:
+ if current[idx]["agent"] != name and name != "Free":
+ if not (room == "train" and idx == 1):
+ logger.error(
+ f'检测到的干员{current[idx]["agent"]},需要安排的干员{name}'
+ )
+ raise Exception("检测到安排干员未成功")
+ else:
+ logger.info(f"任务与当前房间相同,跳过安排{room}人员")
+ finished = True
+ skip_enter = False
+ # 如果完成则移除该任务
+ del plan[room]
+ # back to 基地主界面
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ choose_error += 1
+ self.recog.update()
+ if "检测到漏单!" in str(e):
+ return {}
+ if choose_error > 3:
+ raise e
+ if "检测到安排干员未成功" in str(e):
+ skip_enter = True
+ continue
+ back_count = 0
+ while self.scene() != Scene.INFRA_MAIN:
+ self.back(interval=0.5)
+ back_count += 1
+ if back_count > 3:
raise e
- else:
- continue
+ continue
+ if len(new_plan) != 1:
self.back(0.5)
- if len(in_and_out) > 0:
- replace_plan = {}
- for room in in_and_out:
+ else:
+ if config.conf.run_order_buffer_time <= 0:
+ self.back(0.5)
+ return new_plan
+
+ def agent_arrange(self, plan: tp.BasePlan, get_time=False):
+ logger.info("基建:排班")
+ rooms = list(plan.keys())
+ new_plan = {}
+ # 优先替换工作站再替换宿舍
+ rooms.sort(key=lambda x: x.startswith("dorm"), reverse=False)
+ for room in rooms:
+ new_plan = self.agent_arrange_room(new_plan, room, plan, get_time=get_time)
+ if len(new_plan) == 1:
+ if config.conf.run_order_buffer_time <= 0:
logger.info("开始插拔")
- self.drone(room, True, True)
- in_and_out_plan = [data["agent"] for data in self.current_base[room]]
- # 防止由于意外导致的死循环
- if '但书' in in_and_out_plan or '龙舌兰' in in_and_out_plan:
- in_and_out_plan = [data["agent"] for data in self.currentPlan[room]]
- replace_plan[room] = in_and_out_plan
- self.back(interval=0.5)
- self.back(interval=0.5)
- self.tasks.append({'time': self.tasks[0]['time'], 'plan': replace_plan})
- # 急速换班
- self.todo_task = True
- self.planned = True
- if fia_room != '':
- replace_agent = plan[fia_room][0]
- fia_change_room = self.operators[replace_agent]["room"]
- fia_room_plan = [data["agent"] for data in self.current_base[fia_room]]
- fia_change_room_plan = ['Current']*len(self.currentPlan[fia_change_room])
- fia_change_room_plan[self.operators[replace_agent]["index"]] = replace_agent
+ self.drone(room, not_customize=True)
+ else:
+ # 葛朗台跑单模式
+ error_count = 0
+ while (
+ self.find("factory_accelerate") is None
+ and self.find("bill_accelerate") is None
+ ):
+ if error_count > 5:
+ raise Exception("未成功进入无人机界面")
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.5)
+ error_count += 1
+ # 订单剩余时间
+ execute_time = self.double_read_time(
+ (
+ (
+ int(self.recog.w * 650 / 2496),
+ int(self.recog.h * 660 / 1404),
+ ),
+ (
+ int(self.recog.w * 815 / 2496),
+ int(self.recog.h * 710 / 1404),
+ ),
+ ),
+ use_digit_reader=True,
+ )
+ wait_time = round((execute_time - datetime.now()).total_seconds(), 1)
+ logger.debug(f"停止{wait_time}秒等待订单完成")
+ if 0 < wait_time < config.conf.run_order_delay * 60:
+ logger.info(f"停止{wait_time}秒等待订单完成")
+ self.sleep(wait_time)
+ # 等待服务器交互
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ self.recog.update()
+ # 接受当前订单
+ while (
+ self.find("order_ready", scope=((450, 675), (600, 750))) is not None
+ ):
+ self.tap((self.recog.w * 0.25, self.recog.h * 0.25), interval=0.5)
+ if self.drone_room is None or (
+ self.drone_room == room and room in self.op_data.run_order_rooms
+ ):
+ drone_count = self.digit_reader.get_drone(self.recog.gray)
+ logger.info(f"当前无人机数量为:{drone_count}")
+ # 200 为识别错误
+ if (
+ drone_count >= config.conf.drone_count_limit
+ and drone_count != 201
+ ):
+ self.drone(
+ room, not_return=True, not_customize=True, skip_enter=True
+ )
+ if config.conf.run_order_buffer_time > 0:
+ while self.find("bill_accelerate") is not None:
+ self.back(interval=0.5)
+ else:
+ self.back(interval=0.5)
+ self.back(interval=0.5)
+ # 防止由于意外导致的死循环
+ run_order_room = next(iter(new_plan))
+ if any(
+ any(char in item for item in new_plan[run_order_room])
+ for char in ["但书", "龙舌兰", "佩佩"]
+ ):
+ new_plan[run_order_room] = [
+ data.agent for data in self.op_data.plan[room]
+ ]
+ if config.conf.run_order_buffer_time > 0:
+ self.agent_arrange_room({}, run_order_room, new_plan, skip_enter=True)
+ else:
+ self.tasks.append(
+ SchedulerTask(
+ time=self.tasks[0].time,
+ task_plan=new_plan,
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ )
+ self.skip()
+ elif len(new_plan) > 1:
self.tasks.append(
- {'time': self.tasks[0]['time'], 'plan': {fia_room: fia_room_plan, fia_change_room: fia_change_room_plan}})
+ SchedulerTask(time=self.tasks[0].time, task_plan=new_plan)
+ )
# 急速换班
- self.todo_task = True
+ self.skip()
+ logger.info("返回基建主界面")
+
+ def skip(self, task_names="All"):
+ if task_names == "All":
+ task_names = ["planned", "collect_notification", "todo_task"]
+ if "planned" in task_names:
self.planned = True
- logger.info('返回基建主界面')
- if len(read_time_room) > 0:
- return time_result
+ if "todo_task" in task_names:
+ self.todo_task = True
+ if "collect_notification" in task_names:
+ self.collect_notification = True
- @Asst.CallBackType
+ def no_pending_task(self, minute=0):
+ return self.find_next_task(datetime.now() + timedelta(minutes=minute)) is None
+
+ def reload(self):
+ error = False
+ for room in self.reload_room:
+ try:
+ logger.info(f"开始搓玉补货:{room}")
+ self.enter_room(room)
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.25)
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.25)
+ self.tap((self.recog.w * 0.05, self.recog.h * 0.95), interval=0.25)
+ # 补货
+ self.tap((self.recog.w * 0.75, self.recog.h * 0.3), interval=0.5)
+ self.tap((self.recog.w * 0.75, self.recog.h * 0.9), interval=0.5)
+ if self.scene() in self.waiting_scene:
+ if not self.waiting_solver():
+ return
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ error = True
+ self.recog.update()
+ back_count = 0
+ while self.scene() != Scene.INFRA_MAIN:
+ self.back()
+ back_count += 1
+ if back_count > 3:
+ raise e
+ if not error:
+ self.reload_time = datetime.now()
+
+ @CFUNCTYPE(None, c_int, c_char_p, c_void_p)
def log_maa(msg, details, arg):
m = Message(msg)
- d = json.loads(details.decode('utf-8'))
+ d = json.loads(details.decode("utf-8"))
logger.debug(d)
logger.debug(m)
logger.debug(arg)
+ if "what" in d and d["what"] == "StageDrops":
+ global stage_drop
+ stage_drop["details"].append(d["details"]["drops"])
+ stage_drop["summary"] = d["details"]["stats"]
+
+ elif "what" in d and d["what"] == "RecruitTagsSelected":
+ global recruit_tags_selected
+ recruit_tags_selected["tags"].append(d["details"]["tags"])
+
+ elif "what" in d and d["what"] == "RecruitResult":
+ global recruit_results
+ temp_dict = {
+ "tags": d["details"]["tags"],
+ "level": d["details"]["level"],
+ "result": d["details"]["result"],
+ }
+ recruit_results["results"].append(temp_dict)
+
+ elif "what" in d and d["what"] == "RecruitSpecialTag":
+ global recruit_special_tags
+ recruit_special_tags["tags"].append(d["details"]["tags"])
+ # elif d.get("what") == "DepotInfo" and d["details"].get("done") is True:
+ # logger.info(f"开始扫描仓库(MAA)")
+ # process_itemlist(d)
+
+ def initialize_maa(self):
+ config.stop_maa.clear()
+ conf = config.conf
+ path = pathlib.Path(conf.maa_path)
+ asst_path = os.path.dirname(path / "Python" / "asst")
+ if asst_path not in sys.path:
+ sys.path.append(asst_path)
+ global Message
+
+ try:
+ from asst.asst import Asst
+ from asst.utils import InstanceOptionType, Message
+
+ logger.info("Maa Python模块导入成功")
+ except Exception as e:
+ logger.exception(f"Maa Python模块导入失败:{str(e)}")
+ raise Exception("Maa Python模块导入失败")
+
+ try:
+ logger.debug("开始更新Maa活动关卡导航……")
+ ota_tasks_url = (
+ "https://ota.maa.plus/MaaAssistantArknights/api/resource/tasks.json"
+ )
+ ota_tasks_path = path / "cache" / "resource" / "tasks.json"
+ ota_tasks_path.parent.mkdir(parents=True, exist_ok=True)
+ with urllib.request.urlopen(ota_tasks_url) as u:
+ res = u.read().decode("utf-8")
+ with open(ota_tasks_path, "w", encoding="utf-8") as f:
+ f.write(res)
+ logger.info("Maa活动关卡导航更新成功")
+ except Exception as e:
+ logger.error(f"Maa活动关卡导航更新失败:{str(e)}")
+
+ Asst.load(path=path, incremental_path=path / "cache")
- def inialize_maa(self):
- # 若需要获取详细执行信息,请传入 callback 参数
- # 例如 asst = Asst(callback=my_callback)
- Asst.load(path=self.maa_config['maa_path'])
self.MAA = Asst(callback=self.log_maa)
- # self.MAA.set_instance_option(2, 'maatouch')
- # 请自行配置 adb 环境变量,或修改为 adb 可执行程序的路径
- if self.MAA.connect(self.maa_config['maa_adb_path'], self.ADB_CONNECT):
+ self.stages = []
+ self.MAA.set_instance_option(
+ InstanceOptionType.touch_type, conf.maa_touch_option
+ )
+ if self.MAA.connect(
+ conf.maa_adb_path, self.device.client.device_id, conf.maa_conn_preset
+ ):
logger.info("MAA 连接成功")
else:
logger.info("MAA 连接失败")
raise Exception("MAA 连接失败")
- def maa_plan_solver(self):
+ def append_maa_task(self, type):
+ if type in ["StartUp", "Visit", "Award"]:
+ self.MAA.append_task(type)
+ elif type == "Fight":
+ conf = config.conf
+ _plan = conf.maa_weekly_plan[get_server_weekday()]
+ logger.info(f"现在服务器是{_plan.weekday}")
+ for stage in _plan.stage:
+ logger.info(f"添加关卡:{stage}")
+ self.MAA.append_task(
+ "Fight",
+ {
+ # 空值表示上一次
+ # 'stage': '',
+ "stage": stage,
+ "medicine": _plan.medicine,
+ "stone": 999 if conf.maa_eat_stone else 0,
+ "times": 999,
+ "report_to_penguin": True,
+ "client_type": "",
+ "penguin_id": "",
+ "DrGrandet": False,
+ "server": "CN",
+ "expiring_medicine": 999
+ if conf.exipring_medicine_on_weekend
+ else 0,
+ },
+ )
+ self.stages.append(stage)
+ elif type == "Mall":
+ conf = config.conf
+ self.MAA.append_task(
+ "Mall",
+ {
+ "shopping": True,
+ "buy_first": conf.maa_mall_buy.split(","),
+ "blacklist": conf.maa_mall_blacklist.split(","),
+ "credit_fight": conf.maa_credit_fight
+ and "" not in self.stages
+ and self.credit_fight is None,
+ "force_shopping_if_credit_full": conf.maa_mall_ignore_blacklist_when_full,
+ },
+ )
+
+ def maa_plan_solver(self, tasks="All", one_time=False):
+ """清日常"""
try:
- if self.maa_config['last_execution'] is not None and datetime.now() - timedelta(seconds=self.maa_config['maa_execution_gap']*3600)< self.maa_config['last_execution']:
- logger.info("间隔未超过设定时间,不启动maa")
+ conf = config.conf
+ if (
+ not one_time
+ and self.last_execution["maa"] is not None
+ and (
+ delta := (
+ timedelta(hours=conf.maa_gap)
+ + self.last_execution["maa"]
+ - datetime.now()
+ )
+ )
+ > timedelta()
+ ):
+ logger.info(f"{format_time(delta.total_seconds())}后开始做日常任务")
else:
- self.send_email('休息时长超过9分钟,启动MAA')
+ send_message("启动MAA")
self.back_to_index()
# 任务及参数请参考 docs/集成文档.md
- self.inialize_maa()
- self.MAA.append_task('StartUp')
- _plan= self.maa_config['weekly_plan'][get_server_weekday()]
- logger.info(f"现在服务器是{_plan['weekday']}")
- fights = []
- for stage in _plan["stage"]:
- logger.info(f"添加关卡:{stage}")
- self.MAA.append_task('Fight', {
- # 空值表示上一次
- # 'stage': '',
- 'stage': stage,
- 'medicine': _plan["medicine"],
- 'stone': 0,
- 'times': 999,
- 'report_to_penguin': True,
- 'client_type': '',
- 'penguin_id': '',
- 'DrGrandet': False,
- 'server': 'CN'
- })
- fights.append(stage)
- self.MAA.append_task('Recruit', {
- 'select': [4],
- 'confirm': [3, 4],
- 'times': 4,
- 'refresh': True,
- "recruitment_time": {
- "3": 460,
- "4": 540
- }
- })
- self.MAA.append_task('Visit')
- self.MAA.append_task('Mall', {
- 'shopping': True,
- 'buy_first': ['龙门币', '赤金'],
- 'blacklist': ['家具', '碳', '加急'],
- 'credit_fight':fights[len(fights)-1]!=''
- })
- self.MAA.append_task('Award')
- # asst.append_task('Copilot', {
- # 'stage_name': '千层蛋糕',
- # 'filename': './GA-EX8-raid.json',
- # 'formation': False
-
- # })
+ self.initialize_maa()
+ if tasks == "All":
+ tasks = ["StartUp", "Fight", "Visit", "Mall", "Award"]
+ # tasks = ['StartUp', 'Fight', 'Visit', 'Mall', 'Award', 'Depot']
+ # tasks = ['StartUp', 'Fight', 'Recruit', 'Visit', 'Mall', 'Award']
+ for maa_task in tasks:
+ # if maa_task == "Recruit":
+ # continue
+ self.append_maa_task(maa_task)
self.MAA.start()
- logger.info(f"MAA 启动")
+ stop_time = None
+ if one_time:
+ stop_time = datetime.now() + timedelta(minutes=5)
+ else:
+ global stage_drop
+ stage_drop = {"details": [], "summary": {}}
+
+ logger.info("MAA 启动")
hard_stop = False
while self.MAA.running():
+ # 单次任务默认5分钟
+ if one_time and stop_time < datetime.now():
+ self.MAA.stop()
+ hard_stop = True
# 5分钟之前就停止
- if (self.tasks[0]["time"] - datetime.now()).total_seconds() < 300:
+ elif (
+ not one_time
+ and (self.tasks[0].time - datetime.now()).total_seconds() < 300
+ ):
self.MAA.stop()
hard_stop = True
else:
- time.sleep(0)
- self.send_email('MAA停止')
+ self.sleep(5)
if hard_stop:
- logger.info(f"由于maa任务并未完成,等待3分钟重启软件")
- time.sleep(180)
- self.device.exit('com.hypergryph.arknights')
+ hard_stop_msg = "Maa任务未完成,等待3分钟关闭游戏"
+ logger.info(hard_stop_msg)
+ send_message(hard_stop_msg)
+ self.sleep(180)
+ self.device.exit()
+ if self.device.check_current_focus():
+ self.recog.update()
+ elif not one_time:
+ logger.info("记录MAA 本次执行时间")
+ self.last_execution["maa"] = datetime.now()
+ logger.info(self.last_execution["maa"])
+ if "Mall" in tasks and self.credit_fight is None:
+ self.credit_fight = get_server_weekday()
+ logger.info("记录首次信用作战")
+ logger.debug(stage_drop)
+ # 有掉落东西再发
+ if stage_drop["details"]:
+ send_message(
+ maa_template.render(stage_drop=stage_drop),
+ "Maa停止",
+ )
+
+ else:
+ send_message("Maa单次任务停止")
+ conf = config.conf
+ now_time = datetime.now().time()
+ try:
+ min_time = datetime.strptime(conf.maa_rg_sleep_min, "%H:%M").time()
+ max_time = datetime.strptime(conf.maa_rg_sleep_max, "%H:%M").time()
+ if max_time < min_time:
+ rg_sleep = now_time > min_time or now_time < max_time
else:
- logger.info(f"记录MAA 本次执行时间")
- self.maa_config['last_execution'] = datetime.now()
- logger.info(self.maa_config['last_execution'])
- if self.maa_config['roguelike'] or self.maa_config['reclamation_algorithm'] or self.maa_config[
- 'stationary_security_service']:
- while (self.tasks[0]["time"] - datetime.now()).total_seconds() > 30:
+ rg_sleep = min_time < now_time < max_time
+ except ValueError:
+ rg_sleep = False
+ if (conf.RG or conf.SSS) and not rg_sleep:
+ logger.info("准备开始:肉鸽/保全")
+ send_message("启动 肉鸽/保全")
+ while True:
self.MAA = None
- self.inialize_maa()
- if self.maa_config['roguelike']:
- self.MAA.append_task('Roguelike', {
- 'mode': 0,
- 'starts_count': 9999999,
- 'investment_enabled': True,
- 'investments_count': 9999999,
- 'stop_when_investment_full': False,
- 'squad': '指挥分队',
- 'roles': '取长补短',
- 'theme': 'Mizuki',
- 'core_char': '海沫'
- })
- elif self.maa_config['reclamation_algorithm']:
- self.back_to_maa_config['reclamation_algorithm']()
- self.MAA.append_task('ReclamationAlgorithm')
- # elif self.maa_config['stationary_security_service'] :
- # self.MAA.append_task('SSSCopilot', {
- # 'filename': "F:\\MAA-v4.10.5-win-x64\\resource\\copilot\\SSS_阿卡胡拉丛林.json",
- # 'formation': False,
- # 'loop_times':99
- # })
+ self.initialize_maa()
+ self.recog.update()
+ self.back_to_index()
+ if conf.RG:
+ self.MAA.append_task(
+ "Roguelike",
+ {
+ "theme": conf.maa_rg_theme,
+ "squad": conf.rogue.squad,
+ "roles": conf.rogue.roles,
+ "core_char": conf.rogue.core_char,
+ "use_support": conf.rogue.use_support,
+ "use_nonfriend_support": conf.rogue.use_nonfriend_support,
+ "mode": conf.rogue.mode,
+ "refresh_trader_with_dice": conf.rogue.refresh_trader_with_dice,
+ "starts_count": 9999999,
+ "investments_count": 9999999,
+ "expected_collapsal_paradigms": conf.rogue.expected_collapsal_paradigms,
+ },
+ )
+ elif conf.SSS:
+ copilot = get_path("@app/sss.json")
+ if (
+ not copilot.is_file()
+ or conf.sss.type not in [1, 2]
+ or conf.sss.ec not in [1, 2, 3]
+ ):
+ raise Exception("保全派驻配置错误")
+ if self.to_sss():
+ raise Exception("保全派驻导航失败")
+ self.MAA.append_task(
+ "SSSCopilot",
+ {"filename": str(copilot), "loop_times": 9999999},
+ )
+ logger.info("启动")
self.MAA.start()
+ maa_crash = True
while self.MAA.running():
- if (self.tasks[0]["time"] - datetime.now()).total_seconds() < 30:
+ csleep(5)
+ if (
+ self.tasks[0].time - datetime.now() < timedelta(seconds=30)
+ or config.stop_maa.is_set()
+ ):
+ maa_crash = False
self.MAA.stop()
break
- else:
- time.sleep(0)
- self.device.exit('com.hypergryph.arknights')
- # 生息演算逻辑 结束
- remaining_time = (self.tasks[0]["time"] - datetime.now()).total_seconds()
- logger.info(f"开始休息 {'%.2f' % (remaining_time/60)} 分钟,到{self.tasks[0]['time'].strftime('%H:%M:%S')}")
- self.send_email("脚本停止")
- time.sleep(remaining_time)
+ if maa_crash:
+ self.device.exit()
+ self.check_current_focus()
+ else:
+ break
+
+ elif not rg_sleep:
+ if conf.RA:
+ self.back_to_index()
+ ra_solver = ReclamationAlgorithm(self.device, self.recog)
+ ra_solver.run(self.tasks[0].time - datetime.now())
+ elif conf.SF:
+ self.back_to_index()
+ sf_solver = SecretFront(self.device, self.recog)
+ sf_solver.run(self.tasks[0].time - datetime.now())
+
+ remaining_time = (self.tasks[0].time - datetime.now()).total_seconds()
+ subject = f"休息 {format_time(remaining_time)},到{self.tasks[0].time.strftime('%H:%M:%S')}开始工作"
+ context = f"下一次任务:{self.tasks[0].plan if len(self.tasks[0].plan) != 0 else '空任务' if self.tasks[0].type == '' else self.tasks[0].type}"
+ logger.info(context)
+ logger.info(subject)
+ self.task_count += 1
+ logger.info(f"第{self.task_count}次任务结束")
+ if remaining_time > 0:
+ if remaining_time > 300:
+ if config.conf.close_simulator_when_idle:
+ from arknights_mower.utils.simulator import restart_simulator
+
+ restart_simulator(start=False)
+ elif config.conf.exit_game_when_idle:
+ self.device.exit()
+ self.sleep(remaining_time)
+ self.check_current_focus()
self.MAA = None
+ except MowerExit:
+ if self.MAA is not None:
+ self.MAA.stop()
+ logger.info("停止maa")
+ raise
except Exception as e:
- logger.error(e)
+ logger.exception(e)
self.MAA = None
- remaining_time = (self.tasks[0]["time"] - datetime.now()).total_seconds()
+ self.device.exit()
+ send_message(str(e), "Maa调用出错!", level="ERROR")
+ remaining_time = (self.tasks[0].time - datetime.now()).total_seconds()
if remaining_time > 0:
- logger.info(f"开始休息 {'%.2f' % (remaining_time/60)} 分钟,到{self.tasks[0]['time'].strftime('%H:%M:%S')}")
- time.sleep(remaining_time)
- self.device.exit('com.hypergryph.arknights')
+ logger.info(
+ f"休息 {format_time(remaining_time)},到{self.tasks[0].time.strftime('%H:%M:%S')}开始工作"
+ )
+ self.sleep(remaining_time)
+ self.check_current_focus()
- def send_email(self, tasks):
- return
+ def skland_plan_solover(self):
try:
- msg = MIMEMultipart()
- conntent = str(tasks)
- msg.attach(MIMEText(conntent, 'plain', 'utf-8'))
- msg['Subject'] = self.email_config['subject']
- msg['From'] = self.email_config['account']
- s = smtplib.SMTP_SSL("smtp.qq.com", 465)
- # 登录邮箱
- s.login(self.email_config['account'], self.email_config['pass_code'])
- # 开始发送
- s.sendmail(self.email_config['account'], self.email_config['receipts'], msg.as_string())
- logger.info("邮件发送成功")
+ return SKLand().start()
+ except MowerExit:
+ raise
except Exception as e:
- logger.error("邮件发送失败")
+ logger.exception(f"森空岛签到失败:{e}")
+ send_message(f"森空岛签到失败: {e}", level="ERROR")
+ # 仅尝试一次 不再尝试
+ return (datetime.now() - timedelta(hours=4)).date()
+
+ def recruit_plan_solver(self):
+ if self.last_execution[
+ "recruit"
+ ] is None or datetime.now() > self.last_execution["recruit"] + timedelta(
+ hours=config.conf.recruit_gap
+ ):
+ RecruitSolver(self.device, self.recog).run()
+
+ self.last_execution["recruit"] = datetime.now()
+ logger.info(f"下一次公开招募执行时间在{config.conf.recruit_gap}小时之后")
+
+ def mail_plan_solver(self):
+ if config.conf.check_mail_enable:
+ MailSolver(self.device, self.recog).run()
+ return True
+
+ def report_plan_solver(self):
+ if config.conf.report_enable:
+ return ReportSolver(self.device, self.recog).run()
+
+ def visit_friend_plan_solver(self):
+ if config.conf.visit_friend:
+ return CreditSolver(self.device, self.recog).run()
+
+ def sign_in_plan_solver(self):
+ if not config.conf.sign_in.enable:
+ return
+ # hot_update.update()
+ try:
+ import sign_in
+
+ sign_in_solver = sign_in.SignInSolver(self.device, self.recog)
+ return sign_in_solver.run()
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ return True
+
+ def 仓库扫描(self):
+ try:
+ cultivateDepotSolver().start()
+ DepotSolver(self.device, self.recog).run()
+ except Exception as e:
+ logger.exception(f"先不运行 出bug了 : {e}")
+ return False
+ return True
diff --git a/arknights_mower/solvers/credit.py b/arknights_mower/solvers/credit.py
index aba75abd6..1f4e5197d 100644
--- a/arknights_mower/solvers/credit.py
+++ b/arknights_mower/solvers/credit.py
@@ -1,48 +1,54 @@
-from ..utils import detector
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
+import cv2
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, loadres, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Scene
-class CreditSolver(BaseSolver):
- """
- 通过线索交换自动收集信用
- """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
+class CreditSolver(SceneGraphSolver):
def run(self) -> None:
- logger.info('Start: 信用')
- super().run()
+ logger.info("Start: 访问好友")
+ self.wait_times = 5
+ return super().run()
def transition(self) -> bool:
- if self.scene() == Scene.INDEX:
- self.tap_element('index_friend')
- elif self.scene() == Scene.FRIEND_LIST_OFF:
- self.tap_element('friend_list')
- elif self.scene() == Scene.FRIEND_LIST_ON:
- down = self.find('friend_list_on', strict=True)[1][1]
- scope = [(0, 0), (100000, down)]
- if not self.tap_element('friend_visit', scope=scope, detected=True):
- self.sleep(1)
- elif self.scene() == Scene.FRIEND_VISITING:
- visit_limit = self.find('visit_limit')
- if visit_limit is not None:
- return True
- visit_next = detector.visit_next(self.recog.img)
- if visit_next is not None:
- self.tap(visit_next)
+ if (scene := self.scene()) == Scene.FRIEND_LIST:
+ left, top = 1460, 220
+ img = cropimg(self.recog.gray, ((left, top), (1800, 1000)))
+ img = thres2(img, 245)
+ tpl = loadres("friend_visit", True)
+ result = cv2.matchTemplate(img, tpl, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ h, w = tpl.shape
+ pos = (
+ (min_loc[0] + left, min_loc[1] + top),
+ (min_loc[0] + left + w, min_loc[1] + top + h),
+ )
+ logger.debug(f"{min_val=}, {pos=}")
+ if min_val < 0.5:
+ self.tap(pos)
+ else:
+ self.sleep()
+ elif self.find("visit_limit"):
+ logger.info("今日参与交流已达上限")
+ return True
+ elif scene == Scene.FRIEND_VISITING:
+ if clue_next := self.find("clue_next"):
+ x, y = self.get_pos(clue_next, x_rate=0.5, y_rate=0.85)
+ hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ if abs(hsv[y][x][0] - 12) < 3:
+ self.wait_times = 5
+ self.tap(clue_next)
+ else:
+ return True
else:
- return True
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_social')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
+ if self.wait_times > 0:
+ self.wait_times -= 1
+ self.sleep()
+ else:
+ return True
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown scene')
+ self.scene_graph_navigation(Scene.FRIEND_LIST)
diff --git a/arknights_mower/solvers/credit_fight.py b/arknights_mower/solvers/credit_fight.py
new file mode 100644
index 000000000..9206fa91a
--- /dev/null
+++ b/arknights_mower/solvers/credit_fight.py
@@ -0,0 +1,108 @@
+import cv2
+from scipy.signal import argrelmin
+
+from arknights_mower.solvers.auto_fight import AutoFight
+from arknights_mower.solvers.navigation import NavigationSolver
+from arknights_mower.utils import config
+from arknights_mower.utils.email import send_message
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, loadres
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Scene
+
+
+class CreditFight(SceneGraphSolver):
+ """信用作战
+
+ 从首页导航至OF-1,借助战并自动战斗
+ """
+
+ def run(self):
+ logger.info("Start: 信用作战")
+ self.support = False
+ navi_solver = NavigationSolver(self.device, self.recog)
+ navi_solver.run("OF-1")
+ super().run()
+
+ def choose_support(self):
+ img = cropimg(self.recog.gray, ((0, 908), (1839, 983)))
+ res = loadres("fight/choose", True)
+ result = cv2.matchTemplate(img, res, cv2.TM_SQDIFF_NORMED)[0]
+ threshold = 0.1
+ match = []
+ for i in argrelmin(result, order=100)[0]:
+ if result[i] < threshold:
+ match.append(i)
+ logger.debug(match)
+ x = match[0]
+ return (x, 908), (x + 194, 983)
+
+ def current_squad(self):
+ count = []
+ for i in range(4):
+ img = cropimg(
+ self.recog.img, ((153 + i * 411, 990), (550 + i * 411, (1080)))
+ )
+ hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
+ mask = cv2.inRange(hsv, (97, 0, 0), (101, 255, 255))
+ count.append(cv2.countNonZero(mask))
+ return count.index(max(count)) + 1
+
+ def transition(self):
+ if (scene := self.scene()) == Scene.OPERATOR_BEFORE:
+ if self.recog.gray[65][1333] < 200:
+ self.sleep()
+ return
+ # 取消代理作战
+ if self.recog.gray[907][1600] > 127:
+ self.tap((1776, 908))
+ return
+ self.tap_element("ope_start", interval=2)
+ elif scene == Scene.OPERATOR_SELECT:
+ if self.find("ope_select_start_empty"):
+ logger.info("编队内没有编入干员,停止OF-1")
+ return True
+ squad = self.current_squad()
+ target = config.conf.credit_fight.squad
+ if squad != target:
+ self.tap((target * 411 - 99, 1040))
+ return
+ if self.support:
+ # 开始行动
+ self.tap((1655, 781))
+ fight_solver = AutoFight(self.device, self.recog)
+ conf = config.conf.credit_fight
+ actions = [
+ {"type": "SpeedUp"},
+ {
+ "type": "Deploy",
+ "name": conf.operator,
+ "location": [
+ conf.x,
+ conf.y,
+ ],
+ "direction": conf.direction,
+ },
+ {"type": "SkillDaemon"},
+ ]
+ fight_solver.run("OF-1", [], actions)
+ else:
+ # 借助战
+ self.ctap((1660, 315))
+ elif scene == Scene.OPERATOR_SUPPORT:
+ self.tap(self.choose_support())
+ self.support = True
+ elif scene == Scene.OPERATOR_STRANGER_SUPPORT:
+ self.tap_element("fight/use")
+ elif scene == Scene.OPERATOR_FINISH:
+ return True
+ elif scene == Scene.OPERATOR_FAILED:
+ msg = "OF-1失败,请检查干员,*今天*不会继续进行信用作战"
+ logger.info(msg)
+ send_message(msg, level="ERROR")
+ return True
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ else:
+ navi_solver = NavigationSolver(self.device, self.recog)
+ navi_solver.run("OF-1")
diff --git a/arknights_mower/solvers/cultivate_depot.py b/arknights_mower/solvers/cultivate_depot.py
new file mode 100644
index 000000000..bbf847c50
--- /dev/null
+++ b/arknights_mower/solvers/cultivate_depot.py
@@ -0,0 +1,166 @@
+import hashlib
+import hmac
+import json
+import time
+from urllib import parse
+
+import requests
+
+from arknights_mower.utils import config
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+app_code = "4ca99fa6b56cc2ba"
+
+# 签到url
+sign_url = "https://zonai.skland.com/api/v1/game/attendance"
+# 绑定的角色url
+binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
+# 验证码url
+login_code_url = "https://as.hypergryph.com/general/v1/send_phone_code"
+# 验证码登录
+token_phone_code_url = "https://as.hypergryph.com/user/auth/v2/token_by_phone_code"
+# 密码登录
+token_password_url = "https://as.hypergryph.com/user/auth/v1/token_by_phone_password"
+# 使用token获得认证代码
+grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
+# 使用认证代码获得cred
+cred_code_url = "https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code"
+
+
+class cultivate:
+ def __init__(self):
+ self.record_path = get_path("@app/tmp/cultivate.json")
+ self.header = {
+ "cred": "",
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+ self.header_login = {
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+
+ self.reward = []
+
+ self.header_for_sign = {"platform": "", "timestamp": "", "dId": "", "vName": ""}
+ self.sign_token = ""
+ self.all_recorded = True
+
+ def start(self):
+ for item in config.conf.skland_info:
+ if item.isCheck:
+ self.save_param(self.get_cred_by_token(self.log(item)))
+ for i in self.get_binding_list():
+ if item.cultivate_select == i.get("isOfficial"):
+ body = {"gameId": 1, "uid": i.get("uid")}
+ ingame = f"https://zonai.skland.com/api/v1/game/cultivate/player?uid={i.get('uid')}"
+ resp = requests.get(
+ ingame,
+ headers=self.get_sign_header(
+ ingame, "get", body, self.header
+ ),
+ ).json()
+ with open(self.record_path, "w", encoding="utf-8") as file:
+ json.dump(resp, file, ensure_ascii=False, indent=4)
+
+ def save_param(self, cred_resp):
+ self.header["cred"] = cred_resp["cred"]
+ self.sign_token = cred_resp["token"]
+
+ def log(self, account):
+ r = requests.post(
+ token_password_url,
+ json={"phone": account.account, "password": account.password},
+ headers=self.header_login,
+ ).json()
+ if r.get("status") != 0:
+ raise Exception(f'获得token失败:{r["msg"]}')
+ return r["data"]["token"]
+
+ def get_cred_by_token(self, token):
+ return self.get_cred(self.get_grant_code(token))
+
+ def get_grant_code(self, token):
+ response = requests.post(
+ grant_code_url,
+ json={"appCode": app_code, "token": token, "type": 0},
+ headers=self.header_login,
+ )
+ resp = response.json()
+ if response.status_code != 200:
+ raise Exception(f"获得认证代码失败:{resp}")
+ if resp.get("status") != 0:
+ raise Exception(f'获得认证代码失败:{resp["msg"]}')
+ return resp["data"]["code"]
+
+ def get_cred(self, grant):
+ resp = requests.post(
+ cred_code_url, json={"code": grant, "kind": 1}, headers=self.header_login
+ ).json()
+ if resp["code"] != 0:
+ raise Exception(f'获得cred失败:{resp["message"]}')
+ return resp["data"]
+
+ def get_binding_list(self):
+ v = []
+ resp = requests.get(
+ binding_url,
+ headers=self.get_sign_header(binding_url, "get", None, self.header),
+ ).json()
+
+ if resp["code"] != 0:
+ logger.warning(f"请求角色列表出现问题:{resp['message']}")
+ if resp.get("message") == "用户未登录":
+ logger.warning("用户登录可能失效了,请重新运行此程序!")
+ return []
+ for i in resp["data"]["list"]:
+ if i.get("appCode") != "arknights":
+ continue
+ v.extend(i.get("bindingList"))
+ return v
+
+ def get_sign_header(self, url: str, method, body, old_header):
+ h = json.loads(json.dumps(old_header))
+ p = parse.urlparse(url)
+ if method.lower() == "get":
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, p.query
+ )
+ else:
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, json.dumps(body)
+ )
+ for i in header_ca:
+ h[i] = header_ca[i]
+ return h
+
+ def generate_signature(self, token: str, path, body_or_query):
+ """
+ 获得签名头
+ 接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dId,platform,timestamp,vName).toJSON()
+ 将此字符串做HMAC加密,算法为SHA-256,密钥token为请求cred接口会返回的一个token值
+ 再将加密后的字符串做MD5即得到sign
+ :param token: 拿cred时候的token
+ :param path: 请求路径(不包括网址)
+ :param body_or_query: 如果是GET,则是它的query。POST则为它的body
+ :return: 计算完毕的sign
+ """
+ # 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2
+
+ t = str(int(time.time()) - 2)
+ token = token.encode("utf-8")
+ header_ca = json.loads(json.dumps(self.header_for_sign))
+ header_ca["timestamp"] = t
+ header_ca_str = json.dumps(header_ca, separators=(",", ":"))
+ s = path + body_or_query + t + header_ca_str
+ hex_s = hmac.new(token, s.encode("utf-8"), hashlib.sha256).hexdigest()
+ md5 = (
+ hashlib.md5(hex_s.encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")
+ .decode("utf-8")
+ )
+ return md5, header_ca
diff --git a/arknights_mower/solvers/depotREC.py b/arknights_mower/solvers/depotREC.py
new file mode 100644
index 000000000..6cd4d0a2c
--- /dev/null
+++ b/arknights_mower/solvers/depotREC.py
@@ -0,0 +1,215 @@
+import json
+import lzma
+import os
+import pickle
+import re
+from datetime import datetime
+
+import cv2
+import numpy as np
+import pandas as pd
+from skimage.feature import hog
+
+from arknights_mower.utils.graph import SceneGraphSolver
+
+from .. import __rootdir__
+from ..utils.device.device import Device
+from ..utils.image import loadimg
+from ..utils.log import logger
+from ..utils.path import get_path
+from ..utils.recognize import Recognizer, Scene
+
+# 向下x变大 = 0
+# 向右y变大 = 0
+# 左上角物品y坐标 = 285
+# 左上角物品x坐标 = 187
+# 横排间隔 = 286
+# 竖排间隔 = 234
+# [140:1000, :]
+
+
+def 导入_数字模板():
+ 模板文件夹 = f"{__rootdir__}/resources/depot_num"
+ 数字模板列表 = []
+ for 文件名 in sorted(os.listdir(模板文件夹)):
+ 文件路径 = os.path.join(模板文件夹, 文件名)
+ 数字模板列表.append(loadimg(文件路径, True))
+ return 数字模板列表
+
+
+def 提取特征点(模板):
+ 模板 = 模板[40:173, 40:173]
+ hog_features = hog(
+ 模板,
+ orientations=18,
+ pixels_per_cell=(8, 8),
+ cells_per_block=(2, 2),
+ block_norm="L2-Hys",
+ transform_sqrt=True,
+ channel_axis=2,
+ )
+ return hog_features
+
+
+def 识别空物品(物品灰):
+ 物品灰 = 物品灰[0:130, 130:260]
+ _, 二值图 = cv2.threshold(物品灰, 60, 255, cv2.THRESH_BINARY)
+ 白像素个数 = cv2.countNonZero(二值图)
+ 所有像素个数 = 二值图.shape[0] * 二值图.shape[1]
+ 白像素比值 = int((白像素个数 / 所有像素个数) * 100)
+
+ if 白像素比值 > 99:
+ logger.info("仓库扫描: 删除一次空物品")
+
+ return False
+ else:
+ return True
+
+
+def 切图(圆心x坐标, 圆心y坐标, 拼接结果, 正方形边长=130):
+ 图片 = []
+ for x in 圆心x坐标:
+ for y in 圆心y坐标:
+ 左上角坐标 = (x - 正方形边长, y - 正方形边长)
+ 右下角坐标 = (x + 正方形边长, y + 正方形边长)
+ 正方形 = 拼接结果[
+ 左上角坐标[1] : 右下角坐标[1],
+ 左上角坐标[0] : 右下角坐标[0],
+ ]
+ 正方形灰 = cv2.cvtColor(正方形, cv2.COLOR_RGB2GRAY)
+ if 识别空物品(正方形灰):
+ id = str(datetime.now().timestamp())
+ 正方形切 = 正方形[26:239, 26:239]
+ 图片.append([正方形切, 正方形灰, id])
+ return 图片
+
+
+class depotREC(SceneGraphSolver):
+ def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
+ super().__init__(device, recog)
+
+ start_time = datetime.now()
+
+ # sift = cv2.SIFT_create()
+ orb = cv2.ORB_create()
+ bf = cv2.BFMatcher(cv2.NORM_HAMMING2, crossCheck=True)
+ self.detector = orb
+ self.matcher = bf
+
+ self.仓库输出 = get_path("@app/tmp/depotresult.csv")
+
+ with lzma.open(f"{__rootdir__}/models/CONSUME.pkl", "rb") as pkl:
+ self.knn模型_CONSUME = pickle.load(pkl)
+ with lzma.open(f"{__rootdir__}/models/NORMAL.pkl", "rb") as pkl:
+ self.knn模型_NORMAL = pickle.load(pkl)
+ self.物品数字 = 导入_数字模板()
+
+ self.结果字典 = {}
+
+ logger.info(f"仓库扫描: 吟唱用时{datetime.now() - start_time}")
+
+ def 切图主程序(self, 拼接好的图片):
+ 横坐标 = [188 + 234 * i for i in range(0, 8)]
+ 纵坐标 = [144, 430, 715]
+ 切图列表 = 切图(横坐标, 纵坐标, 拼接好的图片)
+ return 切图列表
+
+ def 读取物品数字(self, 数字图片, 距离阈值=5, 阈值=0.85):
+ 结果 = {}
+ for idx, 模板图片 in enumerate(self.物品数字):
+ res = cv2.matchTemplate(数字图片, 模板图片, cv2.TM_CCORR_NORMED)
+ loc = np.where(res >= 阈值)
+ for i in range(len(loc[0])):
+ pos_x = loc[1][i]
+ accept = True
+ for o in 结果:
+ if abs(o - pos_x) < 距离阈值:
+ accept = False
+ break
+ if accept:
+ 结果[loc[1][i]] = idx
+
+ 物品个数 = ""
+ for k in sorted(结果):
+ 物品个数 += (
+ str(结果[k]) if 结果[k] < 10 else ("万" if 结果[k] == 10 else ".")
+ )
+
+ if not 物品个数:
+ return 999999
+ # # 格式化数字
+ 格式化数字 = int(
+ float("".join(re.findall(r"\d+\.\d+|\d+", 物品个数)))
+ * (10000 if "万" in 物品个数 else 1)
+ )
+ return 格式化数字
+
+ def 匹配物品一次(self, 物品, 物品灰, 模型名称):
+ 物品特征 = 提取特征点(物品)
+ predicted_label = 模型名称.predict([物品特征])
+ 物品数字 = self.读取物品数字(物品灰)
+ return [predicted_label[0], 物品数字]
+
+ def run(self) -> None:
+ logger.info("Start: 仓库扫描")
+ super().run()
+
+ def transition(self) -> bool:
+ logger.info("仓库扫描: 回到桌面")
+ self.back_to_index()
+ if self.scene() == Scene.INDEX:
+ self.tap_index_element("warehouse")
+ logger.info("仓库扫描: 从主界面点击仓库界面")
+
+ time = datetime.now()
+ 任务组 = [
+ (1200, self.knn模型_CONSUME, "消耗物品"),
+ (1400, self.knn模型_NORMAL, "基础物品"),
+ ]
+
+ for 任务 in 任务组:
+ self.tap((任务[0], 70))
+ if not self.find("depot_empty"):
+ self.分类扫描(任务[1])
+ logger.info(
+ f"仓库扫描: {任务[2]}识别,识别用时{datetime.now() - time}"
+ )
+ else:
+ logger.info("仓库扫描: 这个分类下没有物品")
+ logger.info(f"仓库扫描: {self.结果字典}")
+ result = [
+ int(datetime.now().timestamp()),
+ json.dumps(self.结果字典, ensure_ascii=False),
+ {"森空岛输出仅占位": ""},
+ ]
+ depotinfo = pd.DataFrame([result], columns=["Timestamp", "Data", "json"])
+ depotinfo.to_csv(
+ self.仓库输出, mode="a", index=False, header=False, encoding="utf-8"
+ )
+ else:
+ self.back_to_index()
+ return True
+
+ def 对比截图(self, image1, image2):
+ image1 = cv2.cvtColor(image1, cv2.COLOR_RGB2GRAY)
+ image2 = cv2.cvtColor(image2, cv2.COLOR_RGB2GRAY)
+ keypoints1, descriptors1 = self.detector.detectAndCompute(image1, None)
+ keypoints2, descriptors2 = self.detector.detectAndCompute(image2, None)
+ matches = self.matcher.match(descriptors1, descriptors2)
+ similarity = len(matches) / max(len(descriptors1), len(descriptors2))
+ return similarity * 100
+
+ def 分类扫描(self, 模型名称):
+ 截图列表 = []
+ 旧的截图 = self.recog.img
+ 旧的截图 = 旧的截图[140:1000, :]
+ 截图列表.append(旧的截图)
+ self.recog.update()
+ 拼接好的图片 = 截图列表[0]
+ 切图列表 = self.切图主程序(拼接好的图片)
+ logger.info(f"仓库扫描: 需要识别{len(切图列表)}个物品")
+
+ for [物品, 物品灰, id] in 切图列表:
+ [物品名称, 物品数字] = self.匹配物品一次(物品, 物品灰, 模型名称)
+ logger.debug([物品名称, 物品数字])
+ self.结果字典[物品名称] = self.结果字典.get(物品名称, 0) + 物品数字
diff --git a/arknights_mower/solvers/mail.py b/arknights_mower/solvers/mail.py
index 1cf817030..690afc38a 100644
--- a/arknights_mower/solvers/mail.py
+++ b/arknights_mower/solvers/mail.py
@@ -1,43 +1,22 @@
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Scene
-class MailSolver(BaseSolver):
- """
- 收取邮件
- """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
-
+class MailSolver(SceneGraphSolver):
def run(self) -> None:
- # if it touched
self.touched = False
- logger.info('Start: 邮件')
+ logger.info("Start: 领取邮件")
super().run()
def transition(self) -> bool:
- if self.scene() == Scene.INDEX:
- scope = ((0, 0), (100+self.recog.w//4, self.recog.h//10))
- nav = self.find('index_nav', thres=250, scope=scope)
- self.tap(nav, 0.625)
- elif self.scene() == Scene.MAIL:
+ if (scene := self.scene()) == Scene.MAIL:
if self.touched:
return True
self.touched = True
- self.tap_element('read_mail')
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.scene() == Scene.MATERIEL:
- self.tap_element('materiel_ico')
- elif self.get_navigation():
- self.tap_element('nav_index')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
+ self.tap_element("read_mail")
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown scene')
+ self.scene_graph_navigation(Scene.MAIL)
diff --git a/arknights_mower/solvers/mission.py b/arknights_mower/solvers/mission.py
index 4a4b23141..f59d710ee 100644
--- a/arknights_mower/solvers/mission.py
+++ b/arknights_mower/solvers/mission.py
@@ -1,69 +1,46 @@
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Scene
-class MissionSolver(BaseSolver):
- """
- 点击确认完成每日任务和每周任务
- """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
+class MissionSolver(SceneGraphSolver):
+ """每日任务与每周任务"""
def run(self) -> None:
# status of mission completion, 1: daily, 2: weekly
self.checked = 0
- logger.info('Start: 任务')
+ logger.info("Start: 任务")
super().run()
def transition(self) -> bool:
- if self.scene() == Scene.INDEX:
- self.tap_element('index_mission')
- elif self.scene() == Scene.MISSION_TRAINEE:
- if self.checked & 1 == 0:
- self.tap_element('mission_daily')
- elif self.checked & 2 == 0:
- self.tap_element('mission_weekly')
- else:
- return True
- elif self.scene() == Scene.MISSION_DAILY:
+ if (scene := self.scene()) == Scene.MISSION_DAILY:
self.checked |= 1
- collect = self.find('mission_collect')
+ collect = self.find("mission_collect")
if collect is None:
self.sleep(1)
- collect = self.find('mission_collect')
+ collect = self.find("mission_collect")
if collect is not None:
- logger.info('任务:一键收取任务')
+ logger.info("任务:一键收取任务")
self.tap(collect)
elif self.checked & 2 == 0:
- self.tap_element('mission_weekly')
+ self.tap_element("mission_weekly")
else:
return True
- elif self.scene() == Scene.MISSION_WEEKLY:
+ elif scene == Scene.MISSION_WEEKLY:
self.checked |= 2
- collect = self.find('mission_collect')
+ collect = self.find("mission_collect")
if collect is None:
self.sleep(1)
- collect = self.find('mission_collect')
+ collect = self.find("mission_collect")
if collect is not None:
- logger.info('任务:一键收取任务')
+ logger.info("任务:一键收取任务")
self.tap(collect)
elif self.checked & 1 == 0:
- self.tap_element('mission_daily')
+ self.tap_element("mission_daily")
else:
return True
- elif self.scene() == Scene.MATERIEL:
- self.tap_element('materiel_ico')
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_mission')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown scene')
+ self.scene_graph_navigation(Scene.MISSION_DAILY)
diff --git a/arknights_mower/solvers/navigation.py b/arknights_mower/solvers/navigation.py
new file mode 100644
index 000000000..1b5ccedde
--- /dev/null
+++ b/arknights_mower/solvers/navigation.py
@@ -0,0 +1,348 @@
+import re
+
+import cv2
+
+from arknights_mower.models import navigation
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.scene import Scene
+from arknights_mower.utils.vector import va, vs
+
+location = {
+ 1: {
+ "1-1": (0, 0),
+ "1-2": (428, -1),
+ "1-3": (700, 157),
+ "1-4": (1138, 158),
+ "1-5": (1600, 158),
+ "1-6": (2360, -1),
+ "1-7": (3073, -180),
+ "1-8": (3535, -181),
+ "1-9": (4288, -1),
+ "1-10": (4635, 167),
+ "1-11": (4965, -9),
+ "1-12": (5436, -10),
+ },
+ 8: {
+ "R8-1": (0, 0),
+ "R8-2": (471, 0),
+ "R8-3": (864, 0),
+ "R8-4": (1259, 0),
+ "R8-5": (1651, -4),
+ "R8-6": (2045, -4),
+ "R8-7": (2228, -124),
+ "R8-8": (2437, -4),
+ "R8-9": (2951, -4),
+ "R8-10": (3284, -4),
+ "R8-11": (3617, -4),
+ "M8-1": (6, 339),
+ "M8-2": (865, 339),
+ "M8-3": (1259, 339),
+ "M8-4": (1651, 339),
+ "M8-5": (2045, 339),
+ "M8-6": (2439, 340),
+ "M8-7": (2952, 340),
+ "M8-8": (3617, 339),
+ "JT8-1": (4092, 171),
+ "JT8-2": (4545, 171),
+ "JT8-3": (5022, 171),
+ "H8-1": (5556, -24),
+ "H8-2": (5759, 354),
+ "H8-3": (5999, -24),
+ "H8-4": (6192, 354),
+ },
+ 12: {
+ "12-1": (0, 0),
+ "12-2": (342, 292),
+ "12-3": (701, 292),
+ "12-4": (894, 121),
+ "12-5": (1122, 292),
+ "12-6": (1364, 121),
+ "12-7": (1515, 292),
+ "12-8": (2109, 290),
+ "12-9": (2468, 290),
+ "12-10": (2670, 125),
+ "12-11": (2980, 422),
+ "12-12": (3218, 125),
+ "12-13": (3456, 294),
+ "12-14": (3694, 123),
+ "12-15": (4020, 123),
+ "12-16": (4348, -14),
+ "12-17": (4673, -14),
+ "12-18": (4673, 210),
+ "12-19": (5175, 210),
+ "12-20": (5700, 210),
+ "12-21": (6377, 210),
+ },
+ "OF": {
+ "OF-1": (0, 0),
+ "OF-2": (738, 144),
+ "OF-3": (1122, 299),
+ "OF-4": (1475, 135),
+ "OF-5": (2288, -45),
+ "OF-6": (2737, -45),
+ "OF-7": (3550, 135),
+ "OF-8": (3899, 299),
+ },
+ "AP": {
+ "AP-1": (0, 0),
+ "AP-2": (416, -74),
+ "AP-3": (716, -247),
+ "AP-4": (964, -417),
+ "AP-5": (1116, -589),
+ },
+ "LS": {
+ "LS-1": (0, 0),
+ "LS-2": (385, -34),
+ "LS-3": (710, -130),
+ "LS-4": (970, -257),
+ "LS-5": (1138, -421),
+ "LS-6": (1213, -600),
+ },
+ "CA": {
+ "CA-1": (0, 0),
+ "CA-2": (416, -73),
+ "CA-3": (716, -246),
+ "CA-4": (964, -417),
+ "CA-5": (1116, -589),
+ },
+ "CE": {
+ "CE-1": (0, 0),
+ "CE-2": (382, -33),
+ "CE-3": (709, -128),
+ "CE-4": (970, -259),
+ "CE-5": (1136, -420),
+ "CE-6": (1210, -597),
+ },
+ "SK": {
+ "SK-1": (0, 0),
+ "SK-2": (416, -73),
+ "SK-3": (716, -246),
+ "SK-4": (965, -417),
+ "SK-5": (1116, -589),
+ },
+ "PR-A": {"PR-A-1": (0, 0), "PR-A-2": (604, -283)},
+ "PR-B": {"PR-B-1": (0, 0), "PR-B-2": (684, -296)},
+ "PR-C": {"PR-C-1": (0, 0), "PR-C-2": (667, -231)},
+ "PR-D": {"PR-D-1": (0, 0), "PR-D-2": (639, -260)},
+}
+
+collection_prefixs = [
+ "AP",
+ "LS",
+ "CA",
+ "CE",
+ "PR",
+ "SK",
+]
+
+difficulty_str = [
+ "normal",
+ "hard",
+]
+
+
+class NavigationSolver(SceneGraphSolver):
+ def run(self, name: str):
+ logger.info("Start: 关卡导航")
+ self.success = False
+ self.act = None
+
+ # hot_update.update()
+ # if name in hot_update.navigation.NavigationSolver.location:
+ # hot_update.navigation.NavigationSolver(self.device, self.recog).run(name)
+ # return True
+
+ self.name = name
+ prefix = name.split("-")[0]
+ pr_prefix = ""
+ if prefix == "PR":
+ pr_prefix = name.split("-")[1]
+ self.prefix = prefix
+ self.pr_prefix = pr_prefix
+ self.now_difficulty = None
+ self.change_to = None
+ self.patten = r"^(R|JT|H|M)(\d{1,2})$"
+ if name == "Annihilation":
+ logger.info("剿灭导航")
+ elif prefix.isdigit() or re.match(self.patten, prefix):
+ if match := re.search(self.patten, prefix):
+ prefix = match.group(2)
+ prefix = int(prefix)
+ self.prefix = prefix
+ if prefix in location and name in location[prefix]:
+ logger.info(f"主线关卡导航:{name}")
+ if prefix < 4:
+ act = 0
+ elif prefix < 9:
+ act = 1
+ else:
+ act = 2
+ self.act = act
+ else:
+ logger.error(f"暂不支持{name}")
+ return False
+ elif prefix in ["OF"]:
+ logger.info(f'别传关卡导航:"{name}"')
+ elif prefix in ["AP", "LS", "CA", "CE", "SK"]:
+ logger.info(f'资源收集关卡导航:"{name}"')
+ elif prefix.split("-")[0] in ["PR"]:
+ logger.info(f'芯片关卡导航:"{name}"')
+
+ else:
+ logger.error(f"暂不支持{name}")
+ return False
+
+ super().run()
+ return self.success
+
+ def transition(self):
+ if (scene := self.scene()) == Scene.TERMINAL_MAIN:
+ if self.name == "Annihilation":
+ pos_list = [(943, 130), (1491, 130), (1665, 815), (1875, 815)]
+ for pos in pos_list:
+ loaded = self.recog.gray[pos[1]][pos[0]] < 55
+ if loaded:
+ break
+ if not loaded:
+ self.sleep()
+ return
+ if pos := self.find("terminal_eliminate"):
+ self.tap(pos)
+ else:
+ logger.info("本周剿灭已完成")
+ return True
+ elif isinstance(self.prefix, int):
+ self.tap_terminal_button("main_theme")
+ elif self.prefix in ["OF"]:
+ self.tap_terminal_button("biography")
+ elif self.prefix in collection_prefixs:
+ self.tap_terminal_button("collection")
+ elif scene == Scene.OPERATOR_ELIMINATE:
+ if self.name != "Annihilation":
+ self.back()
+ return
+ self.success = True
+ return True
+ elif scene == Scene.TERMINAL_MAIN_THEME:
+ if not isinstance(self.prefix, int):
+ self.back()
+ return
+ act_scope = ((300, 315), (400, 370))
+
+ if self.find(f"navigation/act/{self.act}", scope=act_scope):
+ if pos := self.find(f"navigation/main/{self.prefix}"):
+ self.tap(pos)
+ else:
+ self.device.swipe_ext(
+ ((932, 554), (1425, 554), (1425, 554)), durations=[300, 100]
+ )
+ self.recog.update()
+ else:
+ self.tap((230, 175))
+ elif scene == Scene.TERMINAL_BIOGRAPHY:
+ if self.prefix not in ["OF"]:
+ self.back()
+ return
+ if self.find(f"navigation/biography/{self.prefix}_banner"):
+ self.tap_element("navigation/entry")
+ return
+ self.tap_element(f"navigation/biography/{self.prefix}_entry")
+ elif scene == Scene.TERMINAL_COLLECTION:
+ prefix = self.prefix
+ val = 0.9
+ if self.prefix not in collection_prefixs:
+ self.back()
+ return
+ if self.prefix == "PR":
+ prefix = self.prefix + "-" + self.pr_prefix
+ if self.prefix not in ["LS"]:
+ if self.find(f"navigation/collection/{prefix}_not_available"):
+ logger.info(f"{self.name}未开放")
+ return True
+ if pos := self.find(f"navigation/collection/{prefix}_entry"):
+ self.tap(pos)
+ else:
+ if self.prefix in ["AP", "CA", "CE", "SK"]:
+ self.swipe_noinertia((900, 500), (600, 0))
+ if self.prefix in ["PR"]:
+ self.swipe_noinertia((900, 500), (-600, 0))
+ elif scene == Scene.OPERATOR_CHOOSE_LEVEL:
+ non_black_count = cv2.countNonZero(thres2(self.recog.gray, 10))
+ non_black_ratio = non_black_count / (1920 * 1080)
+ logger.debug(f"{non_black_ratio=}")
+ if non_black_ratio < 0.1:
+ self.sleep()
+ return
+ name, val, loc = "", 1, None
+ prefix = self.prefix
+ # 资源收集关直接按坐标点击
+ if prefix in collection_prefixs:
+ if self.prefix == "PR":
+ prefix = "{}-{}".format(self.prefix, self.pr_prefix)
+ if pos := self.find(f"navigation/collection/{prefix}-1"):
+ self.success = True
+ self.tap(va(pos[0], location[prefix][self.name]))
+ return True
+ # 其余关
+ if self.act == 2:
+ if self.now_difficulty is None:
+ if self.find("navigation/ope_normal"):
+ self.now_difficulty = 0
+ elif self.find("navigation/ope_hard"):
+ self.now_difficulty = 1
+ logger.info(f"当前难度{difficulty_str[self.now_difficulty]}")
+
+ if self.change_to is not None and self.now_difficulty != self.change_to:
+ self.recog.update()
+ if self.find("navigation/ope_difficulty"):
+ self.tap_element(
+ f"navigation/ope_{difficulty_str[self.change_to]}_small"
+ )
+ self.now_difficulty = None
+ else:
+ self.tap_element(
+ f"navigation/ope_{difficulty_str[self.now_difficulty]}"
+ )
+ return
+ for i in location[prefix]:
+ result = cv2.matchTemplate(
+ self.recog.gray, navigation[i], cv2.TM_SQDIFF_NORMED
+ )
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if min_val < val:
+ val = min_val
+ loc = min_loc
+ name = i
+
+ target = va(vs(loc, location[prefix][name]), location[prefix][self.name])
+ if target[0] + 200 > 1920:
+ self.swipe_noinertia((1400, 540), (-800, 0))
+ elif target[0] < 0:
+ self.swipe_noinertia((400, 540), (800, 0))
+ else:
+ self.success = True
+ self.tap(va(target, (60, 20)))
+ elif scene == Scene.OPERATOR_BEFORE:
+ if self.act == 2:
+ if self.change_to is not None:
+ logger.info(f"{self.name} 无法代理")
+ self.success = False
+ self.back_to_index()
+ return True
+ if self.find("ope_agency_lock"):
+ self.change_to = self.now_difficulty ^ 1
+ logger.info(
+ f"{self.name} {difficulty_str[self.now_difficulty]} 无法代理,切难度尝试"
+ )
+ self.back()
+ return
+ if self.success:
+ return True
+ else:
+ self.back()
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ else:
+ self.scene_graph_navigation(Scene.TERMINAL_MAIN)
diff --git a/arknights_mower/solvers/operation.py b/arknights_mower/solvers/operation.py
index c5e71de64..91ccbe1b6 100644
--- a/arknights_mower/solvers/operation.py
+++ b/arknights_mower/solvers/operation.py
@@ -1,516 +1,128 @@
-from __future__ import annotations
-
-import time
-import traceback
-
-from ..data import chapter_list, level_list, weekly_zones, zone_list
-from ..ocr import ocrhandle
-from ..utils import config
-from ..utils import typealias as tp
-from ..utils.image import scope2slice
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Scene
-from ..utils.solver import BaseSolver, StrategyError
-
-BOTTOM_TAP_NUMER = 8
-
-
-class LevelUnopenError(Exception):
- pass
-
-
-class OpeSolver(BaseSolver):
- """
- 自动作战策略
- """
-
- def __init__(self, device=None, recog=None):
- super().__init__(device, recog)
-
- def run(self, level: str = None, times: int = -1, potion: int = 0, originite: int = 0, eliminate: int = 0, plan: list = None):
- """
- :param level: str, 指定关卡,默认为前往上一次关卡或当前界面关卡
- :param times: int, 作战的次数上限,-1 为无限制,默认为 -1
- :param potion: int, 使用药剂恢复体力的次数上限,-1 为无限制,默认为 0
- :param originite: int, 使用源石恢复体力的次数上限,-1 为无限制,默认为 0
- :param eliminate: int, 是否优先处理未完成的每周剿灭,0 为忽略剿灭,1 为优先剿灭,2 为优先剿灭但只消耗代理卡,默认为 0
- :param plan: [[str, int]...], 指定多个关卡以及次数,优先级高于 level
-
- :return remain_plan: [[str, int]...], 未完成的计划
- """
- if level is not None and plan is not None:
- logger.error('不可同时指定 level 和 plan')
- return
- if plan is not None:
- for x in plan:
- if x[0] != 'pre_ope' and (x[0] not in level_list.keys() or level_list[x[0]]['ap_cost'] == 0):
- logger.error(f'不支持关卡 {x[0]},请重新指定')
- return
- if level is not None:
- if level not in level_list.keys() or level_list[level]['ap_cost'] == 0:
- logger.error(f'不支持关卡 {level},请重新指定')
- return
- plan = [[level, times]]
- if plan is None:
- plan = [['pre_ope', times]] # 上一次作战关卡
-
- self.level = level
- self.times = times
- self.potion = potion
- self.originite = originite
- self.eliminate = eliminate
- self.plan = plan
-
- self.recover_state = 0 # 有关体力恢复的状态,0 为未知,1 为体力药剂恢复中,2 为源石恢复中(防止网络波动)
- self.eliminate_state = 0 # 有关每周剿灭的状态,0 为未知,1 为未完成,2 为已完成,3 为未完成但无代理卡可用
- self.wait_pre = 10 # 作战时每次等待的时长,普通关卡为 10s,剿灭关卡为 60s
- self.wait_start = 0 # 作战时第一次等待的时长
- self.wait_total = 0 # 作战时累计等待的时长
- self.level_choosed = plan[0][0] == 'pre_ope' # 是否已经选定关卡
- self.unopen = [] # 未开放的关卡
- self.failed = False # 作战代理是否正常运作
-
- logger.info('Start: 作战')
- logger.debug(f'plan: {plan}')
+from datetime import datetime, timedelta
+from typing import Optional
+
+import cv2
+
+from arknights_mower.models import secret_front
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.datetime import get_server_weekday
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Scene
+
+
+class OperationSolver(SceneGraphSolver):
+ def run(self, stop_time: datetime):
+ logger.info("Start: 代理作战")
+ self.stop_time = stop_time - timedelta(minutes=5)
+ self.sanity_drain = False
super().run()
- return self.plan + self.unopen
-
- def switch_plan(self) -> None:
- self.plan = self.plan[1:]
- self.wait_start = 0
- self.level_choosed = False
-
- def transition(self) -> bool:
- # 选择剩余次数不为 0 的任务
- while len(self.plan) > 0 and self.plan[0][1] == 0:
- self.switch_plan()
- # 如果任务列表为空则退出
- if len(self.plan) == 0:
- return True
-
- if self.scene() == Scene.INDEX:
- self.tap_element('index_terminal')
- elif self.scene() == Scene.TERMINAL_MAIN:
- return self.terminal_main()
- elif self.scene() == Scene.OPERATOR_BEFORE:
- return self.operator_before()
- elif self.scene() == Scene.OPERATOR_ELIMINATE:
- return self.operator_before_elimi()
- elif self.scene() == Scene.OPERATOR_ELIMINATE_AGENCY:
- self.tap_element('ope_elimi_agency_confirm')
- elif self.scene() == Scene.OPERATOR_SELECT:
- self.tap_element('ope_select_start')
- elif self.scene() == Scene.OPERATOR_ONGOING:
- self.ope_ongoing()
- elif self.scene() == Scene.OPERATOR_FINISH:
- self.ope_finish()
- elif self.scene() == Scene.OPERATOR_ELIMINATE_FINISH:
- self.ope_finish_elimi()
- elif self.scene() == Scene.OPERATOR_GIVEUP: # TODO 得找个稳定复现代理三星变两星的地图
- logger.error('代理出现失误')
- return True
- elif self.scene() == Scene.OPERATOR_FAILED:
- logger.error('代理出现失误')
- self.failed = True
- self.tap((self.recog.w // 2, 10))
- elif self.scene() == Scene.OPERATOR_RECOVER_POTION:
- return self.recover_potion()
- elif self.scene() == Scene.OPERATOR_RECOVER_ORIGINITE:
- return self.recover_originite()
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.scene() == Scene.UPGRADE:
- self.tap_element('upgrade')
- elif self.scene() == Scene.OPERATOR_DROP:
- self.tap_element('nav_button', 0.2)
- elif self.get_navigation():
- self.tap_element('nav_terminal')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
- else:
- raise RecognizeError('Unknown scene')
-
- def terminal_main(self) -> bool:
- if self.eliminate_state != 3:
- eliminate_todo = self.find('terminal_eliminate')
- # 检查每周剿灭完成情况
- if eliminate_todo is not None:
- self.eliminate_state = 1
- else:
- self.eliminate_state = 2
- # 如果每周剿灭未完成且设定为优先处理
- if self.eliminate and eliminate_todo is not None:
- self.tap(eliminate_todo)
+ return self.sanity_drain
+
+ def number(self, scope: tp.Scope, height: Optional[int] = None):
+ img = cropimg(self.recog.gray, scope)
+ if height:
+ scale = 25 / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, 127)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+
+ value = 0
+
+ for x, y, w, h in rect:
+ if w < 5 or h < 5:
+ continue
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ score = []
+ for i in range(10):
+ im = secret_front[i]
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+
+ return value
+
+ def transition(self):
+ if (scene := self.scene()) == Scene.OPERATOR_BEFORE:
+ if datetime.now() > self.stop_time:
+ return True
+ if self.recog.gray[65][1333] < 200:
+ self.sleep()
return
- try:
- # 选择关卡
- self.choose_level(self.plan[0][0])
- except LevelUnopenError:
- logger.error(f'关卡 {self.plan[0][0]} 未开放,请重新指定')
- self.unopen.append(self.plan[0])
- self.switch_plan()
- return
- self.level_choosed = True
-
- def operator_before(self) -> bool:
- # 关卡未选定,退回到终端主界面选择关卡
- if not self.level_choosed:
- self.get_navigation()
- self.tap_element('nav_terminal')
- return
- # 代理出现过失误,终止作战
- if self.failed:
- return True
- # 激活代理作战
- agency = self.find('ope_agency')
- if agency is not None:
- self.tap(agency)
- return
- # 重置普通关卡等待时长
- if self.wait_pre != 10:
- self.wait_start = 0
- self.wait_pre = 10
- self.wait_total = 0
- # 点击开始作战
- # ope_start_SN 对应三周年活动愚人船的 UI
- ope_start = self.find('ope_start', judge=False) or self.find('ope_start_SN', judge=False)
- if ope_start:
- self.tap(ope_start)
- # 确定可以开始作战后扣除相应的消耗药剂或者源石
- if self.recover_state == 1:
- logger.info('use potion to recover sanity')
- self.potion -= 1
- elif self.recover_state == 2:
- logger.info('use originite to recover sanity')
- self.originite -= 1
- self.recover_state = 0
- else:
- # 与预期不符,等待一阵并重新截图
- self.sleep(1)
-
- def operator_before_elimi(self) -> bool:
- # 如果每周剿灭完成情况未知,退回到终端主界面选择关卡
- if self.eliminate_state == 0:
- self.get_navigation()
- self.tap_element('nav_terminal')
- return
- # 如果每周剿灭已完成但仍然在剿灭关卡前,则只可能是 pre_ope 为剿灭关卡,此时应该退出
- if self.eliminate_state == 2:
- logger.warning('检测到关卡为剿灭,但每周剿灭任务已完成')
- return True
- # 如果剿灭代理卡已经用完但仍然在剿灭关卡前,则只可能是 pre_ope 为剿灭关卡,此时应该退出
- if self.eliminate_state == 3:
- logger.warning('检测到关卡为剿灭,但剿灭代理卡已经用完')
- return True
- # 代理出现过失误,终止作战
- if self.failed:
- return True
- # 激活代理作战
- agency = self.find('ope_elimi_agency')
- if agency is not None:
- self.tap(agency)
- return
- agency = self.find('ope_agency')
- if agency is not None:
- self.tap(agency)
- return
- # 若只想用代理卡,但此时代理卡已经用光,则退回到终端主界面选择关卡
- if self.eliminate == 2 and self.find('ope_elimi_agenct_used') is None:
- self.eliminate_state = 3
- self.get_navigation()
- self.tap_element('nav_terminal')
- return
- # 重置剿灭关卡等待时长
- if self.wait_pre != 60:
- self.wait_start = 0
- self.wait_pre = 60
- self.wait_total = 0
- # 点击开始作战
- self.tap_element('ope_start')
- # 确定可以开始作战后扣除相应的消耗药剂或者源石
- if self.recover_state == 1:
- logger.info('use potion to recover sanity')
- self.potion -= 1
- elif self.recover_state == 2:
- logger.info('use originite to recover sanity')
- self.originite -= 1
- self.recover_state = 0
-
- def ope_ongoing(self) -> None:
- if self.wait_total < self.wait_start:
- if self.wait_total == 0:
- logger.info(f'等待 {self.wait_start} 秒')
- self.wait_total += self.wait_pre
- if self.wait_total == self.wait_start:
- self.sleep(self.wait_pre)
- else:
- time.sleep(self.wait_pre)
- else:
- logger.info(f'等待 {self.wait_pre} 秒')
- self.wait_total += self.wait_pre
- self.sleep(self.wait_pre)
-
- def ope_finish(self) -> None:
- # 更新 wait_start
- if self.wait_total > 0:
- if self.wait_start == 0:
- self.wait_start = self.wait_total - self.wait_pre
- else:
- self.wait_start = min(
- self.wait_start + self.wait_pre, self.wait_total - self.wait_pre)
- self.wait_total = 0
- # 如果关卡选定则扣除任务次数
- if self.level_choosed:
- self.plan[0][1] -= 1
- # 若每周剿灭未完成则剿灭完成状态变为未知
- if self.eliminate_state == 1:
- self.eliminate_state = 0
- # 随便点击某处退出结算界面
- self.tap((self.recog.w // 2, 10))
-
- def ope_finish_elimi(self) -> None:
- # 每周剿灭完成情况变为未知
- self.eliminate_state = 0
- # 随便点击某处退出结算界面
- self.tap((self.recog.w // 2, 10))
-
- def recover_potion(self) -> bool:
- if self.potion == 0:
- if self.originite != 0:
- # 转而去使用源石恢复
- self.tap_element('ope_recover_originite')
+ if self.recog.gray[907][1600] < 127:
+ self.tap((1776, 908))
return
- # 关闭恢复界面
- self.back()
- return True
- elif self.recover_state:
- # 正在恢复中,防止网络波动
- self.sleep(3)
- else:
- # 选择药剂恢复体力
- if self.find('ope_recover_potion_empty') is not None:
- # 使用次数未归零但已经没有药剂可以恢复体力了
- logger.info('The potions have been used up.')
- self.potion = 0
+ repeat = self.number(((1520, 890), (1545, 930)), 28)
+ if repeat > 1:
+ self.tap((1500, 910))
+ self.tap((1500, 801))
return
- self.tap_element('ope_recover_potion_choose', 0.9, 0.75, judge=False)
- # 修改状态
- self.recover_state = 1
-
- def recover_originite(self) -> bool:
- if self.originite == 0:
- if self.potion != 0:
- # 转而去使用药剂恢复
- self.tap_element('ope_recover_potion')
+ self.tap_element("ope_start", interval=2)
+ elif scene == Scene.OPERATOR_SELECT:
+ self.tap((1655, 781))
+ elif scene == Scene.OPERATOR_FINISH:
+ # TODO: 掉落识别
+ self.tap((310, 330))
+ elif scene == Scene.OPERATOR_FAILED:
+ self.tap((310, 330))
+ elif scene == Scene.OPERATOR_ONGOING:
+ if self.find("ope_agency_fail"):
+ self.tap((121, 79))
+ else:
+ self.sleep(10)
+ elif scene == Scene.OPERATOR_GIVEUP:
+ self.tap_element("double_confirm/main", x_rate=1)
+ elif scene == Scene.OPERATOR_RECOVER_POTION:
+ use_medicine = False
+ # 先看设置是否吃药
+ if config.conf.maa_expiring_medicine:
+ if config.conf.exipring_medicine_on_weekend:
+ use_medicine = get_server_weekday() >= 5
+ else:
+ use_medicine = True
+ # 再看是否有药可吃
+ if use_medicine:
+ img = cropimg(self.recog.img, ((1015, 515), (1170, 560)))
+ img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
+ img = cv2.inRange(img, (170, 0, 0), (174, 255, 255))
+ count = cv2.countNonZero(img)
+ logger.debug(f"{count=}")
+ use_medicine = count > 3000
+ if use_medicine:
+ logger.info("使用即将过期的理智药")
+ self.tap((1635, 865))
return
- # 关闭恢复界面
- self.back()
+ else:
+ self.sanity_drain = True
+ return True
+ elif scene == Scene.OPERATOR_RECOVER_ORIGINITE:
+ self.sanity_drain = True
return True
- elif self.recover_state:
- # 正在恢复中,防止网络波动
- self.sleep(3)
- else:
- # 选择源石恢复体力
- if self.find('ope_recover_originite_empty') is not None:
- # 使用次数未归零但已经没有源石可以恢复体力了
- logger.info('The originites have been used up.')
- self.originite = 0
+ elif scene == Scene.OPERATOR_ELIMINATE:
+ if self.find("ope_agency_lock"):
+ logger.error("无法代理当期剿灭")
+ self.sanity_drain = True
+ return True
+ if self.find("1800"):
+ logger.info("本周剿灭已完成")
+ self.sanity_drain = True
+ return True
+ if pos := self.find("ope_elimi_agency"):
+ self.tap(pos)
return
- self.tap_element('ope_recover_originite_choose', 0.9, 0.85, judge=False)
- # 修改状态
- self.recover_state = 2
-
- def ocr_level(self) -> list:
- ocr = ocrhandle.predict(self.recog.img)
- ocr = list(filter(lambda x: x[1] in level_list.keys(), ocr))
- levels = sorted([x[1] for x in ocr])
- return ocr, levels
-
- def choose_level(self, level: str) -> None:
- """ 在终端主界面选择关卡 """
- if level == 'pre_ope':
- logger.info(f'前往上一次关卡')
- self.tap_element('terminal_pre')
- return
-
- zone_name = level_list[level]['zone_id']
- zone = zone_list[zone_name]
- logger.info(f'关卡:{level}')
- logger.info(f'章节:{zone["name"]}')
-
- # 识别导航栏,辅助识别章节
- scope = self.recog.nav_button()
- scope[1][1] = self.recog.h
-
- # 选择章节/区域
- if zone['type'] == 'MAINLINE':
- self.switch_bottom(1)
- self.choose_zone_theme(zone, scope)
- elif zone['type'] == 'BRANCHLINE':
- self.switch_bottom(2)
- self.choose_zone_supple(zone, scope)
- elif zone['type'] == 'SIDESTORY':
- self.switch_bottom(3)
- self.choose_zone_supple(zone, scope)
- elif zone['type'] == 'WEEKLY':
- self.switch_bottom(4)
- self.choose_zone_resource(zone)
+ self.tap_element("ope_start", interval=2)
+ elif scene == Scene.OPERATOR_ELIMINATE_AGENCY:
+ self.tap_element("ope_elimi_agency_confirm", interval=2)
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown zone')
-
- # 关卡选择核心逻辑
- ocr, levels = self.ocr_level()
-
- # 先向左滑动
- retry_times = 3
- while level not in levels:
- _levels = levels
- self.swipe_noinertia((self.recog.w // 2, self.recog.h // 4),
- (self.recog.w // 3, 0), 100)
- ocr, levels = self.ocr_level()
- if _levels == levels:
- retry_times -= 1
- if retry_times == 0:
- break
- else:
- retry_times = 3
-
- # 再向右滑动
- retry_times = 3
- while level not in levels:
- _levels = levels
- self.swipe_noinertia((self.recog.w // 2, self.recog.h // 4),
- (-self.recog.w // 3, 0), 100)
- ocr, levels = self.ocr_level()
- if _levels == levels:
- retry_times -= 1
- if retry_times == 0:
- break
- else:
- retry_times = 3
-
- # 如果正常运行则此时关卡已经出现在界面中
- for x in ocr:
- if x[1] == level:
- self.tap(x[2])
- return
- raise RecognizeError('Level recognition error')
-
- def switch_bottom(self, id: int) -> None:
- id = id * 2 + 1
- bottom = self.recog.h - 10
- self.tap((self.recog.w//BOTTOM_TAP_NUMER//2*id, bottom))
-
- def choose_zone_theme(self, zone: list, scope: tp.Scope) -> None:
- """ 识别主题曲区域 """
- # 定位 Chapter 编号
- ocr = []
- act_id = 999
- while act_id != zone['chapterIndex']:
- _act_id = act_id
- act_id = -1
- for x in ocr:
- if zone['chapterIndex'] < _act_id:
- if x[1].upper().replace(' ', '') == chapter_list[_act_id-1].replace(' ', ''):
- self.tap(x[2])
- break
- else:
- if x[1].upper().replace(' ', '') == chapter_list[_act_id+1].replace(' ', ''):
- self.tap(x[2])
- break
- ocr = ocrhandle.predict(self.recog.img[scope2slice(scope)])
- for x in ocr:
- if x[1][:7].upper() == 'EPISODE' and len(x[1]) == 9:
- try:
- episode = int(x[1][-2:])
- act_id = zone_list[f'main_{episode}']['chapterIndex']
- break
- except Exception:
- raise RecognizeError('Unknown episode')
- if act_id == -1 or _act_id == act_id:
- raise RecognizeError('Unknown error')
-
- # 定位 Episode 编号
- cover = self.find(f'main_{episode}')
- while zone['zoneIndex'] < episode:
- self.swipe_noinertia((cover[0][0], cover[0][1]),
- (cover[1][0] - cover[0][0], 0))
- episode -= 1
- while episode < zone['zoneIndex']:
- self.swipe_noinertia((cover[1][0], cover[0][1]),
- (cover[0][0] - cover[1][0], 0))
- episode += 1
- self.tap(cover)
-
- def choose_zone_supple(self, zone: list, scope: tp.Scope) -> None:
- """ 识别别传/插曲区域 """
- try_times = 5
- zoneIndex = {}
- for x in zone_list.values():
- zoneIndex[x['name'].replace('·', '')] = x['zoneIndex']
- while try_times:
- try_times -= 1
- ocr = ocrhandle.predict(self.recog.img[scope2slice(scope)])
- zones = set()
- for x in ocr:
- if x[1] in zoneIndex.keys():
- zones.add(zoneIndex[x[1]])
- logger.debug(zones)
- if zone['zoneIndex'] in zones:
- for x in ocr:
- if x[1] == zone['name'].replace('·', ''):
- self.tap(x[2])
- self.tap_element('enter')
- return
- raise RecognizeError
- else:
- st, ed = None, None
- for x in ocr:
- if x[1] in zoneIndex.keys() and zoneIndex[x[1]] == min(zones):
- ed = x[2][0]
- elif x[1] in zoneIndex.keys() and zoneIndex[x[1]] == max(zones):
- st = x[2][0]
- logger.debug((st, ed))
- self.swipe_noinertia(st, (0, ed[1]-st[1]))
-
- def choose_zone_resource(self, zone: list) -> None:
- """ 识别资源收集区域 """
- ocr = ocrhandle.predict(self.recog.img)
- unable = list(filter(lambda x: x[1] in ['不可进入', '本日16:00开启'], ocr))
- ocr = list(filter(lambda x: x[1] in weekly_zones, ocr))
- weekly = sorted([x[1] for x in ocr])
- while zone['name'] not in weekly:
- _weekly = weekly
- self.swipe((self.recog.w // 4, self.recog.h // 4),
- (self.recog.w // 16, 0))
- ocr = ocrhandle.predict(self.recog.img)
- unable = list(filter(lambda x: x[1] in ['不可进入', '本日16:00开启'], ocr))
- ocr = list(filter(lambda x: x[1] in weekly_zones, ocr))
- weekly = sorted([x[1] for x in ocr])
- if _weekly == weekly:
- break
- while zone['name'] not in weekly:
- _weekly = weekly
- self.swipe((self.recog.w // 4, self.recog.h // 4),
- (-self.recog.w // 16, 0))
- ocr = ocrhandle.predict(self.recog.img)
- unable = list(filter(lambda x: x[1] in ['不可进入', '本日16:00开启'], ocr))
- ocr = list(filter(lambda x: x[1] in weekly_zones, ocr))
- weekly = sorted([x[1] for x in ocr])
- if _weekly == weekly:
- break
- if zone['name'] not in weekly:
- raise RecognizeError('Not as expected')
- for x in ocr:
- if x[1] == zone['name']:
- for item in unable:
- if x[2][0][0] < item[2][0][0] < x[2][1][0]:
- raise LevelUnopenError
- self.tap(x[2])
- ocr = ocrhandle.predict(self.recog.img)
- unable = list(filter(lambda x: x[1] == '关卡尚未开放', ocr))
- if len(unable):
- raise LevelUnopenError
- break
+ self.sanity_drain = False
+ return True
diff --git a/arknights_mower/solvers/reclamation_algorithm.py b/arknights_mower/solvers/reclamation_algorithm.py
new file mode 100644
index 000000000..6d8352054
--- /dev/null
+++ b/arknights_mower/solvers/reclamation_algorithm.py
@@ -0,0 +1,629 @@
+import math
+from datetime import datetime, timedelta
+from threading import Event, Thread
+from typing import Optional
+
+import cv2
+import numpy as np
+
+from arknights_mower.utils import config, rapidocr
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.image import cropimg, loadres, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.matcher import Matcher
+from arknights_mower.utils.scene import Scene
+from arknights_mower.utils.solver import BaseSolver
+
+src_pts = np.float32([[0, 97], [1920, 97], [-400, 1080], [2320, 1080]])
+dst_pts = np.float32([[0, 0], [1920, 0], [0, 1000], [1920, 1000]])
+trans_mat = cv2.getPerspectiveTransform(src_pts, dst_pts)
+rev_mat = np.linalg.inv(trans_mat)
+
+
+class Map:
+ def __init__(self, img: tp.GrayImage):
+ self.map = cv2.warpPerspective(img, trans_mat, (1920, 1000))
+ self.matcher = None
+
+ def map2scrn(self, point: tp.Coordinate) -> tp.Coordinate:
+ x = np.array([[point[0]], [point[1]], [1]])
+ y = np.dot(rev_mat, x)
+ return (round(y[0][0] / y[2][0]), round(y[1][0] / y[2][0]))
+
+ def find(self, res: tp.Res) -> Optional[tp.Scope]:
+ logger.debug(f"find: {res}")
+ if self.matcher is None:
+ self.matcher = Matcher(self.map)
+ res_img = loadres(f"ra/map/{res}", True)
+ return self.matcher.match(res_img, scope=((250, 0), (1620, 900)), prescore=0.5)
+
+
+class ReclamationAlgorithm(BaseSolver):
+ fast_tap_scenes = [Scene.RA_GUIDE_DIALOG]
+ places = {
+ "base": [[1623, 588], [1943, 906]],
+ "奇遇_砾沙平原": [[563, 1348], [819, 1433]],
+ "奇遇_崎岖窄路": [[485, 1764], [740, 1848]],
+ "奇遇_风啸峡谷": [[2177, 1218], [2434, 1303]],
+ "冲突区_丢失的订单": [[1741, 1291], [2022, 1375]],
+ "捕猎区_聚羽之地": [[1020, 761], [1276, 888]],
+ "资源区_射程以内": [[171, 1611], [429, 1738]],
+ "资源区_林中寻宝": [[864, 1044], [1122, 1174]],
+ "要塞_征税的选择": [[755, 1560], [1036, 1644]],
+ "后舍_众人会聚之地": [[1992, 521], [2334, 643]],
+ }
+ drag_scope = ((250, 10), (1630, 895))
+
+ def run(
+ self,
+ duration: Optional[timedelta] = None,
+ ) -> None:
+ logger.info("Start: 生息演算")
+
+ self.timeout = timedelta(seconds=config.conf.reclamation_algorithm.timeout)
+ self.deadline = datetime.now() + duration - self.timeout if duration else None
+
+ self.battle_wait = 0 # 进入战斗后等待剧情出现
+
+ self.thread = None
+ self.event = Event()
+ self.unknown_time = None
+
+ self.battle_task = None
+ self.task_queue = None
+ self.in_adventure = None
+ self.ap = None
+ self.vp = None
+
+ self.left_kitchen = False
+
+ super().run()
+
+ def tap_loop(self, pos):
+ while not self.event.is_set():
+ self.device.tap(pos)
+
+ def fast_tap(self, pos):
+ self.event.clear()
+ if not self.thread:
+ self.thread = Thread(target=self.tap_loop, args=(pos,))
+ self.thread.start()
+ logger.debug(f"开始快速点击{pos}")
+ self.sleep()
+
+ def stop_fast_tap(self):
+ self.event.set()
+ if self.thread:
+ self.thread.join()
+ self.thread = None
+ logger.debug("快速点击已停止")
+
+ def detect_adventure(self, map, pos):
+ adventures = [i for i in self.places if i.startswith("奇遇")]
+ scores = []
+ img = cropimg(
+ map,
+ (
+ (pos[0][0] + 140, pos[0][1] + 25),
+ (pos[1][0] + 10, pos[1][1] - 15),
+ ),
+ )
+ img = thres2(img, 180)
+ for i in adventures:
+ template = loadres(f"ra/map/{i}", True)
+ template = cropimg(template, ((151, 36), (254, 62)))
+ template = thres2(template, 180)
+ result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF)
+ result = cv2.minMaxLoc(result)[1]
+ scores.append(result)
+ return adventures[scores.index(max(scores))]
+
+ def find_place(self, place=None):
+ map = Map(self.recog.gray)
+ if place is None:
+ for place in self.places:
+ if pos := map.find(place):
+ break
+ if pos is None:
+ logger.debug("在当前地图中没有找到任何地点")
+ return None, None
+ if place.startswith("奇遇"):
+ place = self.detect_adventure(map.map, pos)
+ pos = map.map2scrn(pos[0]), map.map2scrn(pos[1])
+ logger.debug(f"在区域{pos}识别到任意地点{place}")
+ return pos, place
+ elif place.startswith("奇遇"):
+ while True:
+ pos = map.find(place)
+ if pos is None:
+ logger.debug(f"没有找到地点{place}")
+ return None
+ if place == self.detect_adventure(map.map, pos):
+ pos = map.map2scrn(pos[0]), map.map2scrn(pos[1])
+ logger.debug(f"在区域{pos}识别到指定地点{place}")
+ return pos
+ else:
+ cv2.rectangle(map.map, pos[0], pos[1], (255,), -1)
+ map.matcher = Matcher(map.map)
+ else:
+ pos = map.find(place)
+ if pos is None:
+ logger.debug(f"没有找到地点{place}")
+ return None
+ pos = map.map2scrn(pos[0]), map.map2scrn(pos[1])
+ logger.debug(f"在区域{pos}识别到指定地点{place}")
+ return pos
+
+ def drag(
+ self, res: str, position: tp.Coordinate = (960, 500), update_vp: bool = True
+ ) -> bool:
+ res_img = loadres(f"ra/map/{res}", True)
+ top_left = (
+ position[0] - round(res_img.shape[1] / 2),
+ position[1] - round(res_img.shape[0] / 2),
+ )
+ if update_vp:
+ pos, place = self.find_place()
+ if pos is None:
+ logger.warning("没有找到任何地点,拖拽失败")
+ return False
+ vp1 = tuple(a - b for a, b in zip(self.places[place][0], pos[0]))
+ else:
+ vp1 = self.vp
+ vp2 = tuple(a - b for a, b in zip(self.places[res][0], top_left))
+ self.vp = vp2
+ vp_offset = tuple(a - b for a, b in zip(vp1, vp2))
+ total_distance = tuple(abs(a) for a in vp_offset)
+ max_drag = tuple(b - a for a, b in zip(*self.drag_scope))
+ steps = max(math.ceil(d / m) for d, m in zip(total_distance, max_drag))
+ if steps == 0:
+ logger.warning("拖拽距离异常")
+ return False
+ step_distance = tuple(round(i / steps) for i in vp_offset)
+ center_point = tuple(round((a + b) / 2) for a, b in zip(*self.drag_scope))
+ map = Map(self.recog.gray)
+ start_point = map.map2scrn(
+ tuple(a - round(b / 2) for a, b in zip(center_point, step_distance))
+ )
+ end_point = map.map2scrn(
+ tuple(a + round(b / 2) for a, b in zip(center_point, step_distance))
+ )
+ for _ in range(steps):
+ self.device.swipe_ext(
+ (start_point, end_point, end_point), durations=[500, 200]
+ )
+ self.sleep(0.1)
+ return True
+
+ def plan_task(self):
+ self.task_queue = []
+ if not self.drag("奇遇_风啸峡谷"):
+ self.map_back()
+ return
+ if self.find_place("冲突区_丢失的订单"):
+ logger.debug("奇遇_风啸峡谷已完成")
+ else:
+ logger.debug("添加任务:奇遇_风啸峡谷")
+ self.task_queue.append("奇遇_风啸峡谷")
+ self.drag("资源区_射程以内", update_vp=False)
+ if self.find_place("要塞_征税的选择"):
+ logger.debug("左侧区域任务已完成")
+ elif self.find_place("奇遇_崎岖窄路"):
+ logger.debug("添加任务:奇遇_崎岖窄路")
+ self.task_queue.append("奇遇_崎岖窄路")
+ elif self.find_place("资源区_射程以内"):
+ logger.debug("添加任务:资源区_射程以内、奇遇_崎岖窄路")
+ self.task_queue += ["资源区_射程以内", "奇遇_崎岖窄路"]
+ elif self.find_place("奇遇_砾沙平原"):
+ logger.debug("添加任务:奇遇_砾沙平原,资源区_射程以内,奇遇_崎岖窄路")
+ self.task_queue += ["奇遇_砾沙平原", "资源区_射程以内", "奇遇_崎岖窄路"]
+ else:
+ self.drag("资源区_林中寻宝", update_vp=False)
+ if self.find_place("资源区_林中寻宝"):
+ logger.debug(
+ "添加任务:资源区_林中寻宝,奇遇_砾沙平原,资源区_射程以内,奇遇_崎岖窄路"
+ )
+ self.task_queue += [
+ "资源区_林中寻宝",
+ "奇遇_砾沙平原",
+ "资源区_射程以内",
+ "奇遇_崎岖窄路",
+ ]
+ else:
+ logger.debug(
+ "添加任务:捕猎区_聚羽之地,资源区_林中寻宝,奇遇_砾沙平原,资源区_射程以内,奇遇_崎岖窄路"
+ )
+ self.task_queue += [
+ "捕猎区_聚羽之地",
+ "资源区_林中寻宝",
+ "奇遇_砾沙平原",
+ "资源区_射程以内",
+ "奇遇_崎岖窄路",
+ ]
+
+ def detect_prepared(self) -> int:
+ templates = [f"ra/prepared_{i}" for i in range(3)]
+ scores = [
+ self.template_match(i, ((510, 820), (700, 900)))[0] for i in templates
+ ]
+ result = scores.index(max(scores))
+ logger.debug(f"已准备 {result}")
+ return result
+
+ def detect_day(self) -> int:
+ templates = ["ra/day_next"] + [f"ra/day_{i}" for i in range(1, 5)]
+ scores = [
+ self.template_match(i, ((1730, 110), (1805, 175)))[0] for i in templates
+ ]
+ result = scores.index(max(scores))
+ logger.debug(f"第{result}天" if result > 0 else "进入下一天")
+ return result
+
+ def detect_ap(self) -> int:
+ ap = 0
+ if self.get_color((1752, 182))[0] > 175:
+ ap += 1
+ if self.get_color((1774, 182))[0] > 175:
+ ap += 1
+ logger.debug(f"决断次数:{ap}")
+ return ap
+
+ def detect_drink(self) -> int:
+ templates = [f"ra/drink_{i}" for i in range(0, 6, 2)]
+ scores = [
+ self.template_match(i, ((1448, 1004), (1465, 1028)))[0] for i in templates
+ ]
+ result = scores.index(max(scores)) * 2
+ logger.debug(f"饮料数量:{result}")
+ return result
+
+ def map_skip_day(self, reason: str):
+ logger.debug(f"{reason},跳过行动,进入下一天")
+ self.tap((1764, 134), interval=0.5)
+
+ def print_ap(self):
+ logger.debug(f"剩余决断次数:{self.ap}")
+
+ def map_back(self):
+ self.tap_element("ra/map_back", thres=200)
+
+ def detect_score(self, scope=None, find_max=True):
+ if find_max and self.find("ra/max", scope=scope, score=0.7):
+ return "已达上限"
+ score = rapidocr.engine(
+ thres2(cropimg(self.recog.gray, scope), 127),
+ use_det=False,
+ use_cls=False,
+ use_rec=True,
+ )[0][0][0]
+ return score or "识别失败"
+
+ def map_select_place(self, pos, place):
+ if popup := self.find("ra/popup"):
+ if (
+ popup[0][0] > pos[1][0]
+ or popup[1][0] < pos[0][0]
+ or popup[0][1] > pos[1][1]
+ or popup[1][1] < pos[0][1]
+ ):
+ x = int((pos[0][0] + pos[1][0]) / 2)
+ else:
+ logger.info(f"{place}被虫子挡住了!")
+ if popup[0][0] < pos[1][0] < popup[1][0]:
+ x = int((pos[0][0] + popup[0][0]) / 2)
+ elif popup[0][0] < pos[0][0] < popup[1][0]:
+ x = int((popup[1][0] + pos[1][0]) / 2)
+ else:
+ ll = popup[0][0] - pos[0][0]
+ lr = pos[1][0] - popup[1][0]
+ if ll > lr:
+ x = int(pos[0][0] + ll / 2)
+ else:
+ x = int(popup[1][0] + lr / 2)
+ else:
+ x = int((pos[0][0] + pos[1][0]) / 2)
+ self.tap((x, int((pos[0][1] + pos[1][1]) / 2)), interval=0.5)
+
+ def move_forward(self, scene):
+ # 从首页进入生息演算主页
+ if scene == Scene.CONNECTING:
+ self.sleep()
+ elif scene == Scene.INDEX:
+ self.tap_index_element("terminal")
+ elif scene == Scene.TERMINAL_MAIN:
+ self.tap_terminal_button("longterm")
+ elif scene == Scene.TERMINAL_LONGTERM:
+ self.tap_element("terminal_longterm_reclamation_algorithm")
+
+ # 从生息演算主页进入生息演算
+ elif scene == Scene.RA_MAIN:
+ # 等动画
+ if pos := self.find("ra/start_button"):
+ self.tap(pos, interval=8)
+ else:
+ self.tap_element("ra/continue_button", interval=3)
+
+ # 剧情
+ elif scene == Scene.RA_GUIDE_ENTRANCE:
+ pos = self.find("ra/guide_entrance")
+ self.tap(pos, x_rate=2, y_rate=1.5, interval=0.5)
+ elif scene == Scene.RA_GUIDE_BATTLE_ENTRANCE:
+ self.battle_wait = 3
+ self.tap_element("ra/start_action", interval=5)
+ elif scene == Scene.RA_GUIDE_DIALOG:
+ self.battle_wait = 0
+ self.fast_tap((1631, 675))
+
+ # 进入与退出战斗
+ elif scene == Scene.RA_BATTLE_ENTRANCE:
+ if self.battle_task in self.task_queue:
+ self.task_queue.remove(self.battle_task)
+ self.ap -= 1
+ self.tap_element("ra/start_action")
+ elif scene == Scene.RA_BATTLE:
+ if self.battle_wait > 0:
+ self.battle_wait -= 1
+ self.sleep()
+ else:
+ if pos := self.recog.find_ra_battle_exit():
+ self.tap(pos, interval=0.5)
+ else:
+ self.recog.update()
+ elif scene == Scene.RA_BATTLE_EXIT_CONFIRM:
+ self.tap_element("ra/confirm_green", interval=0.5)
+ elif scene == Scene.RA_BATTLE_COMPLETE:
+ self.tap_element("ra/battle_complete", interval=0.5)
+
+ # 结算界面
+ elif scene == Scene.RA_DAY_COMPLETE:
+ if pos := self.find("ra/period_complete_start_new_day"):
+ self.tap(pos)
+ else:
+ self.tap((960, 900))
+ elif scene == Scene.RA_PERIOD_COMPLETE:
+ scope_list = (
+ (((860, 550), (1060, 640)), "生息总结", False),
+ (((870, 785), (956, 825)), "转化技术点数", True),
+ (((1250, 785), (1345, 825)), "转化繁荣点数", True),
+ )
+ for scope, title, find_max in scope_list:
+ score = self.detect_score(scope=scope, find_max=find_max)
+ logger.info(f"{title}:{score}")
+ self.tap((960, 230))
+
+ # 存档操作
+ elif scene == Scene.RA_DELETE_SAVE_DIALOG:
+ if pos := self.find("ra/delete_save_confirm_dialog_ok_button"):
+ self.tap(pos, interval=0.5)
+ self.tap(pos)
+ self.task_queue = None
+ self.ap = None
+ else:
+ self.sleep()
+
+ # 奇遇
+ elif scene == Scene.RA_ADVENTURE:
+ if not self.in_adventure:
+ self.in_adventure = self.task_queue[0]
+
+ leave_adventure = False
+ if self.find("ra/no_enough_resources"):
+ leave_adventure = True
+ logger.debug("所需资源不足")
+ elif self.find("ra/spring"):
+ leave_adventure = True
+ logger.debug("特殊处理涌泉奇遇")
+ elif self.find("ra/shop"):
+ leave_adventure = True
+ logger.debug("特殊处理巡回杂货铺奇遇")
+
+ if leave_adventure:
+ if self.in_adventure in self.task_queue:
+ self.task_queue.remove(self.in_adventure)
+ if self.in_adventure == "奇遇_砾沙平原":
+ self.task_queue.remove("资源区_射程以内")
+ self.task_queue.remove("奇遇_崎岖窄路")
+ self.map_back()
+ else:
+ tpl = loadres("ra/ap-1", True)
+ tpl = thres2(tpl, 127)
+ w, h = tpl.shape[::-1]
+ scope = ((1640, 400), (1900, 900))
+ x, y = scope[0]
+ img = thres2(self.recog.gray, 127)
+ img = cropimg(img, scope)
+ res = cv2.matchTemplate(img, tpl, cv2.TM_CCOEFF_NORMED)
+ threshold = 0.8
+ loc = np.where(res >= threshold)
+ scope = tuple(
+ ((a + x, b + y), (a + x + w, b + y + h)) for a, b in zip(*loc[::-1])
+ )
+ if scope:
+ self.tap(scope[-1], interval=0.5)
+ self.tap(scope[-1])
+ elif pos := self.find("ra/adventure_ok"):
+ if self.in_adventure in self.task_queue:
+ self.task_queue.remove(self.in_adventure)
+ self.ap -= 1
+ pos = (1740, round((pos[0][1] + pos[1][1]) / 2))
+ self.tap(pos, interval=0.5)
+ self.tap(pos)
+ else:
+ self.tap((428, 411), interval=0.5)
+ self.tap((428, 411))
+
+ # 地图页操作
+ elif scene == Scene.RA_MAP:
+ if self.in_adventure:
+ if self.in_adventure not in self.task_queue:
+ self.in_adventure = None
+ self.recog.update()
+ return
+ day = self.detect_day()
+ if day == 0:
+ self.tap((1760, 140), interval=2)
+ self.ap = 2
+ return
+ if day == 4:
+ score, pos = self.template_match(
+ "ra/delete_save", scope=((1610, 820), (1785, 940))
+ )
+ if score > 0.9:
+ self.tap(pos, interval=0.5)
+ else:
+ self.ctap((1540, 1010), max_seconds=5)
+ return
+ if self.ap is None:
+ self.ap = self.detect_ap()
+ self.print_ap()
+ if self.task_queue is None:
+ if day == 1 and self.ap == 2:
+ logger.debug("初始化任务列表")
+ self.task_queue = [
+ "奇遇_风啸峡谷",
+ "捕猎区_聚羽之地",
+ "资源区_林中寻宝",
+ "奇遇_砾沙平原",
+ "资源区_射程以内",
+ "奇遇_崎岖窄路",
+ ]
+ else:
+ self.plan_task()
+ if self.ap == 0:
+ self.map_skip_day("当日已无决断次数")
+ return
+ remain_ap = (3 - day) * 2 + self.ap
+ if remain_ap - len(self.task_queue) >= 2:
+ self.map_skip_day("当日无任务")
+ return
+ if self.ap == 1 and len(self.task_queue) + 1 == remain_ap:
+ self.map_skip_day("当日无任务")
+ return
+ logger.debug(self.task_queue)
+ place = self.task_queue[0]
+ pos = self.find_place(place)
+ if pos is None:
+ if self.drag(place):
+ pos = self.find_place(place)
+ else:
+ # 返回首页重新进入,使基地位于屏幕中央
+ self.map_back()
+ return
+ self.map_select_place(pos, place)
+ if place.startswith("奇遇"):
+ self.tap((428, 411), interval=0.5)
+ else:
+ self.battle_task = place
+ self.recog.update()
+ elif scene == Scene.RA_DAY_DETAIL:
+ self.tap_element("ra/waste_time_button", interval=0.5)
+ elif scene == Scene.RA_WASTE_TIME_DIALOG:
+ self.tap_element("ra/confirm_green")
+
+ # 作战编队
+ elif scene == Scene.RA_SQUAD_EDIT:
+ if self.detect_drink() == 0:
+ self.left_kitchen = False
+ self.tap((1430, 1015), interval=0.5)
+ else:
+ self.tap_element("ra/squad_edit_start_button", interval=0.5)
+ elif scene == Scene.RA_SQUAD_EDIT_DIALOG:
+ self.tap_element("ra/confirm_red", interval=6)
+ elif scene == Scene.RA_SQUAD_ABNORMAL:
+ self.tap_element("ra/confirm_red", interval=6)
+
+ # 烹饪台
+ elif scene == Scene.RA_KITCHEN:
+ if self.left_kitchen:
+ self.tap_element("ra/return_from_kitchen", x_rate=0.07)
+ return
+ last_drink = self.detect_prepared()
+ while last_drink < 2:
+ self.tap_element("ra/auto+1", interval=0.5)
+ drink = self.detect_prepared()
+ if drink == last_drink:
+ logger.debug("饮料无法合成,返回地图,清空任务列表")
+ self.task_queue = []
+ self.tap_element("ra/return_from_kitchen", x_rate=0.07)
+ self.tap_element("ra/squad_back")
+ self.map_back()
+ return
+ last_drink = drink
+ self.tap_element("ra/cook_button", interval=0.5)
+
+ # 能量饮料不足
+ elif scene == Scene.RA_INSUFFICIENT_DRINK:
+ self.tap_element("ra/dialog_cancel")
+
+ # 获得物资
+ elif scene == Scene.RA_GET_ITEM:
+ if pos := self.find("ra/click_to_continue"):
+ self.tap(pos, interval=0.5)
+ if self.in_adventure:
+ self.sleep(0.5)
+ self.tap((428, 411), interval=0.5)
+ else:
+ self.left_kitchen = True
+ self.tap_element("ra/return_from_kitchen", x_rate=0.07)
+ else:
+ self.sleep(0.5)
+
+ # 一张便条
+ elif scene == Scene.RA_NOTICE:
+ self.tap((1366, 620), interval=0.5)
+ self.tap((1366, 620))
+
+ else:
+ self.sleep()
+
+ def back_to_index(self, scene):
+ if scene == Scene.CONNECTING:
+ self.sleep()
+ elif scene in [Scene.RA_MAIN, Scene.TERMINAL_LONGTERM, Scene.TERMINAL_MAIN]:
+ self.tap_element("nav_button", x_rate=0.21)
+ elif scene in [Scene.RA_MAP, Scene.RA_DAY_DETAIL, Scene.RA_BATTLE_ENTRANCE]:
+ self.map_back()
+ elif scene == Scene.RA_SQUAD_EDIT:
+ self.tap_element("ra/squad_back")
+ elif scene == Scene.RA_KITCHEN:
+ self.tap_element("ra/return_from_kitchen", x_rate=0.07)
+ elif scene in [Scene.RA_SQUAD_EDIT_DIALOG, Scene.RA_WASTE_TIME_DIALOG]:
+ self.tap_element("ra/dialog_cancel")
+ elif 900 < scene < 1000:
+ self.move_forward(scene)
+ else:
+ self.sleep()
+
+ def transition(self) -> bool:
+ if (scene := self.ra_scene()) not in self.fast_tap_scenes:
+ self.stop_fast_tap()
+
+ now = datetime.now()
+
+ if scene == Scene.UNKNOWN:
+ if not self.unknown_time:
+ self.unknown_time = now
+ elif now - self.unknown_time > self.timeout:
+ logger.warning("连续识别到未知场景")
+ try:
+ self.task_queue = None
+ self.in_adventure = False
+ self.recog.scene = Scene.UNDEFINED
+ if self.scene() != Scene.UNKNOWN:
+ super().back_to_index()
+ else:
+ self.device.exit()
+ self.check_current_focus()
+ except MowerExit:
+ raise
+ else:
+ self.unknown_time = None
+
+ if self.deadline and self.deadline < datetime.now():
+ if scene == Scene.INDEX:
+ return True
+ else:
+ self.back_to_index(scene)
+ else:
+ self.move_forward(scene)
diff --git a/arknights_mower/solvers/record.py b/arknights_mower/solvers/record.py
new file mode 100644
index 000000000..e968f9270
--- /dev/null
+++ b/arknights_mower/solvers/record.py
@@ -0,0 +1,234 @@
+# 用于记录Mower操作行为
+import sqlite3
+from datetime import datetime
+
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+
+# 记录干员进出站以及心情数据,将记录信息存入agent_action表里
+def save_action_to_sqlite_decorator(func):
+ def wrapper(self, name, mood, current_room, current_index, update_time=False):
+ agent = self.operators[name] # 干员
+
+ agent_current_room = agent.current_room # 干员所在房间
+ agent_is_high = agent.is_high() # 是否高优先级
+
+ # 调用原函数
+ result = func(self, name, mood, current_room, current_index, update_time)
+ if not update_time:
+ return
+ # 保存到数据库
+ current_time = datetime.now()
+ database_path = get_path("@app/tmp/data.db")
+
+ try:
+ # Create 'tmp' directory if it doesn't exist
+ get_path("@app/tmp").mkdir(exist_ok=True)
+
+ connection = sqlite3.connect(database_path)
+ cursor = connection.cursor()
+
+ # Create a table if it doesn't exist
+ cursor.execute(
+ "CREATE TABLE IF NOT EXISTS agent_action ("
+ "name TEXT,"
+ "agent_current_room TEXT,"
+ "current_room TEXT,"
+ "is_high INTEGER,"
+ "agent_group TEXT,"
+ "mood REAL,"
+ "current_time TEXT"
+ ")"
+ )
+
+ # Insert data
+ cursor.execute(
+ "INSERT INTO agent_action VALUES (?, ?, ?, ?, ?, ?, ?)",
+ (
+ name,
+ agent_current_room,
+ current_room,
+ int(agent_is_high),
+ agent.group,
+ mood,
+ str(current_time),
+ ),
+ )
+
+ connection.commit()
+ connection.close()
+
+ # Log the action
+ logger.debug(
+ f"Saved action to SQLite: Name: {name}, Agent's Room: {agent_current_room}, Agent's group: {agent.group}, "
+ f"Current Room: {current_room}, Is High: {agent_is_high}, Current Time: {current_time}"
+ )
+
+ except sqlite3.Error as e:
+ logger.error(f"SQLite error: {e}")
+
+ return result
+
+ return wrapper
+
+
+def get_work_rest_ratios():
+ # TODO 整理数据计算工休比
+ database_path = get_path("@app/tmp/data.db")
+
+ try:
+ # 连接到数据库
+ conn = sqlite3.connect(database_path)
+ # conn = sqlite3.connect('../../tmp/data.db')
+ cursor = conn.cursor()
+ # 查询数据
+ cursor.execute("""
+ SELECT a.*
+ FROM agent_action a
+ JOIN (
+ SELECT DISTINCT b.name
+ FROM agent_action b
+ WHERE DATE(b.current_time) >= DATE('now', '-7 day', 'localtime')
+ AND b.is_high = 1 AND b.current_room NOT LIKE 'dormitory%'
+ UNION
+ SELECT '菲亚梅塔' AS name
+ ) AS subquery ON a.name = subquery.name
+ WHERE DATE(a.current_time) >= DATE('now', '-1 month', 'localtime')
+ ORDER BY a.current_time;
+ """)
+ data = cursor.fetchall()
+ # 关闭数据库连接
+ conn.close()
+ except sqlite3.Error:
+ data = []
+ processed_data = {}
+ grouped_data = {}
+ for row in data:
+ name = row[0]
+ current_room = row[2]
+ current_time = row[6] # Assuming index 6 is the current_time column
+ agent = grouped_data.get(
+ name,
+ {
+ "agent_data": [
+ {"current_time": current_time, "current_room": current_room}
+ ],
+ "difference": [],
+ },
+ )
+ difference = {
+ "time_diff": calculate_time_difference(
+ agent["agent_data"][-1]["current_time"], current_time
+ ),
+ "current_room": agent["agent_data"][-1]["current_room"],
+ }
+ agent["agent_data"].append(
+ {"current_time": current_time, "current_room": current_room}
+ )
+ agent["difference"].append(difference)
+ grouped_data[name] = agent
+ for name in grouped_data:
+ work_time = 0
+ rest_time = 0
+ for difference in grouped_data[name]["difference"]:
+ if difference["current_room"].startswith("dormitory"):
+ rest_time += difference["time_diff"]
+ else:
+ work_time += difference["time_diff"]
+ processed_data[name] = {
+ "labels": ["休息时间", "工作时间"],
+ "datasets": [{"data": [rest_time, work_time]}],
+ }
+
+ return processed_data
+
+
+# 整理心情曲线
+def get_mood_ratios():
+ database_path = get_path("@app/tmp/data.db")
+
+ try:
+ # 连接到数据库
+ conn = sqlite3.connect(database_path)
+ cursor = conn.cursor()
+ # 查询数据(筛掉宿管和替班组的数据)
+ cursor.execute("""
+ SELECT a.*
+ FROM agent_action a
+ JOIN (
+ SELECT DISTINCT b.name
+ FROM agent_action b
+ WHERE DATE(b.current_time) >= DATE('now', '-7 day', 'localtime')
+ AND b.is_high = 1 AND b.current_room NOT LIKE 'dormitory%'
+ UNION
+ SELECT '菲亚梅塔' AS name
+ ) AS subquery ON a.name = subquery.name
+ WHERE DATE(a.current_time) >= DATE('now', '-7 day', 'localtime')
+ ORDER BY a.agent_group DESC, a.current_time;
+
+ """)
+ data = cursor.fetchall()
+ # 关闭数据库连接
+ conn.close()
+ except sqlite3.Error:
+ data = []
+
+ work_rest_data_ratios = get_work_rest_ratios()
+ grouped_data = {}
+ grouped_work_rest_data = {}
+ for row in data:
+ group_name = row[4] # Assuming 'agent_group' is at index 4
+ if not group_name:
+ group_name = row[0]
+ mood_data = grouped_data.get(group_name, {"labels": [], "datasets": []})
+ work_rest_data = grouped_work_rest_data.get(
+ group_name, work_rest_data_ratios[row[0]]
+ )
+ grouped_work_rest_data[group_name] = work_rest_data
+
+ timestamp_datetime = datetime.strptime(
+ row[6], "%Y-%m-%d %H:%M:%S.%f"
+ ) # Assuming 'current_time' is at index 6
+ # 创建 Luxon 格式的字符串
+ current_time = f"{timestamp_datetime.year:04d}-{timestamp_datetime.month:02d}-{timestamp_datetime.day:02d}T{timestamp_datetime.hour:02d}:{timestamp_datetime.minute:02d}:{timestamp_datetime.second:02d}.{timestamp_datetime.microsecond:06d}+08:00"
+
+ mood_label = row[0] # Assuming 'name' is at index 0
+ mood_value = row[5] # Assuming 'mood' is at index 5
+
+ if mood_label in [dataset["label"] for dataset in mood_data["datasets"]]:
+ # if mood_label == mood_data['datasets'][0]['label']:
+ mood_data["labels"].append(current_time)
+ # If mood label already exists, find the corresponding dataset
+ for dataset in mood_data["datasets"]:
+ if dataset["label"] == mood_label:
+ dataset["data"].append({"x": current_time, "y": mood_value})
+ break
+ else:
+ # If mood label doesn't exist, create a new dataset
+ mood_data["labels"].append(current_time)
+ mood_data["datasets"].append(
+ {"label": mood_label, "data": [{"x": current_time, "y": mood_value}]}
+ )
+
+ grouped_data[group_name] = mood_data
+ print(grouped_work_rest_data)
+ # 将数据格式整理为数组
+ formatted_data = []
+ for group_name, mood_data in grouped_data.items():
+ formatted_data.append(
+ {
+ "groupName": group_name,
+ "moodData": mood_data,
+ "workRestData": grouped_work_rest_data[group_name],
+ }
+ )
+ return formatted_data
+
+
+def calculate_time_difference(start_time, end_time):
+ time_format = "%Y-%m-%d %H:%M:%S.%f"
+ start_datetime = datetime.strptime(start_time, time_format)
+ end_datetime = datetime.strptime(end_time, time_format)
+ time_difference = end_datetime - start_datetime
+ return time_difference.total_seconds()
diff --git a/arknights_mower/solvers/recruit.py b/arknights_mower/solvers/recruit.py
index 1b2e33094..150bb7901 100644
--- a/arknights_mower/solvers/recruit.py
+++ b/arknights_mower/solvers/recruit.py
@@ -1,352 +1,518 @@
-from __future__ import annotations
-
-from ..data import recruit_agent, recruit_tag
-from ..ocr import ocr_rectify, ocrhandle
-from ..utils import segment
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Recognizer, Scene
-from ..utils.solver import BaseSolver
-
-
-class RecruitPoss(object):
- """ 记录公招标签组合的可能性数据 """
-
- def __init__(self, choose: int, max: int = 0, min: int = 7) -> None:
- self.choose = choose # 标签选择(按位),第 6 个标志位表示是否选满招募时限,0 为选满,1 为选 03:50
- self.max = max # 等级上限
- self.min = min # 等级下限
- self.poss = 0 # 可能性
- self.lv2a3 = False # 是否包含等级为 2 和 3 的干员
- self.ls = [] # 可能的干员列表
-
- def __lt__(self, another: RecruitPoss) -> bool:
- return (self.poss) < (another.poss)
-
- def __str__(self) -> str:
- return "%s,%s,%s,%s,%s" % (self.choose, self.max, self.min, self.poss, self.ls)
-
- def __repr__(self) -> str:
- return "%s,%s,%s,%s,%s" % (self.choose, self.max, self.min, self.poss, self.ls)
-
-
-class RecruitSolver(BaseSolver):
- """
- 自动进行公招
- """
-
+import lzma
+import pickle
+from itertools import combinations
+
+import cv2
+
+from arknights_mower import __rootdir__
+from arknights_mower.data import (
+ agent_with_tags,
+ recruit_agent,
+)
+from arknights_mower.utils import config
+from arknights_mower.utils.device.device import Device
+from arknights_mower.utils.email import recruit_rarity, recruit_template, send_message
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import Recognizer, Scene
+from arknights_mower.utils.vector import va
+
+with lzma.open(f"{__rootdir__}/models/riic_base_digits.pkl", "rb") as f:
+ number = pickle.load(f)
+with lzma.open(f"{__rootdir__}/models/recruit_result.pkl", "rb") as f:
+ recruit_res_template = pickle.load(f)
+with lzma.open(f"{__rootdir__}/models/recruit.pkl", "rb") as f:
+ tag_template = pickle.load(f)
+job_list = [
+ "recruit/riic_res/CASTER",
+ "recruit/riic_res/MEDIC",
+ "recruit/riic_res/PIONEER",
+ "recruit/riic_res/SPECIAL",
+ "recruit/riic_res/SNIPER",
+ "recruit/riic_res/SUPPORT",
+ "recruit/riic_res/TANK",
+ "recruit/riic_res/WARRIOR",
+]
+
+
+class RecruitSolver(SceneGraphSolver):
def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
super().__init__(device, recog)
-
- def run(self, priority: list[str] = None) -> None:
- """
- :param priority: list[str], 优先考虑的公招干员,默认为高稀有度优先
- """
- self.priority = priority
- self.recruiting = 0
- self.has_ticket = True # 默认含有招募票
- self.can_refresh = True # 默认可以刷新
-
- logger.info('Start: 公招')
- logger.info(f'目标干员:{priority if priority else "无,高稀有度优先"}')
+ self.find_scope = {
+ "recruit/begin_recruit": [(340, 200), (590, 300)],
+ "recruit/job_requirements": [(100, 20), (300, 100)],
+ "recruit/recruit_done": [(300, 250), (600, 340)],
+ "recruit/recruit_lock": [(400, 120), (540, 220)],
+ }
+
+ # 四个招募栏位的位置 固定1920*1080所以直接写死了
+ up = 270
+ down = 1060
+ left = 25
+ right = 1890
+
+ self.segments = {
+ 1: [(left, up), (950, 650)],
+ 2: [(970, up), (right, 650)],
+ 3: [(left, 690), (950, 650)],
+ 4: [(970, 690), (right, down)],
+ }
+ self.agent_choose = {}
+ self.recruit_index = 1
+ # 默认要支援机械
+ self.recruit_order_index = 2
+ self.recruit_order = [6, 5, 1, 4, 3, 2]
+
+ self.result_agent = {}
+ self.ticket_number = None
+
+ def run(self):
+ self.add_recruit_param()
super().run()
+ logger.info(self.result_agent)
+
+ if self.agent_choose:
+ logger.info("开包汇总如下")
+ for pos in self.agent_choose:
+ agent = []
+ for item in self.agent_choose[pos]["result"]:
+ agent.append(item["name"])
+ logger.info(
+ "{}:[".format(pos)
+ + ",".join(self.agent_choose[pos]["tags"])
+ + "]:{}".format(",".join(agent))
+ )
+ if self.agent_choose or self.result_agent:
+ logger.info("招募汇总如下")
+ send_message(
+ recruit_template.render(
+ recruit_results=self.agent_choose,
+ recruit_get_agent=self.result_agent,
+ permit_count=config.conf.recruitment_permit,
+ title_text="公招汇总",
+ ),
+ "公招汇总通知",
+ "INFO",
+ )
+ return self.agent_choose, self.result_agent
def transition(self) -> bool:
- if self.scene() == Scene.INDEX:
- self.tap_element('index_recruit')
- elif self.scene() == Scene.RECRUIT_MAIN:
- segments = segment.recruit(self.recog.img)
- tapped = False
- for idx, seg in enumerate(segments):
- if self.recruiting & (1 << idx) != 0:
- continue
- if self.tap_element('recruit_finish', scope=seg, detected=True):
- tapped = True
- break
- if not self.has_ticket and not self.can_refresh:
- continue
- required = self.find('job_requirements', scope=seg)
- if required is None:
- self.tap(seg)
- tapped = True
- self.recruiting |= (1 << idx)
- break
- if not tapped:
+ if (scene := self.scene()) == Scene.RECRUIT_MAIN:
+ if self.recruit_index > 4:
+ logger.info("结束公招")
+ return True
+ job_requirements_scope = [
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/job_requirements"][0],
+ ),
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/job_requirements"][1],
+ ),
+ ]
+ begin_recruit_scope = [
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/begin_recruit"][0],
+ ),
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/begin_recruit"][1],
+ ),
+ ]
+ recruit_done_scope = [
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/recruit_done"][0],
+ ),
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/recruit_done"][1],
+ ),
+ ]
+ recruit_lock_scope = [
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/recruit_lock"][0],
+ ),
+ va(
+ self.segments[self.recruit_index][0],
+ self.find_scope["recruit/recruit_lock"][1],
+ ),
+ ]
+
+ if self.find("recruit/job_requirements", scope=job_requirements_scope):
+ self.recruit_index = self.recruit_index + 1
+ logger.debug(f"{self.recruit_index}正在招募")
+ return
+ elif pos := self.find("recruit/recruit_lock", scope=recruit_lock_scope):
+ logger.debug(f"{self.recruit_index}锁定")
return True
- elif self.scene() == Scene.RECRUIT_TAGS:
+ elif pos := self.find("recruit/recruit_done", scope=recruit_done_scope):
+ logger.debug(f"{self.recruit_index}结束招募 开包")
+ self.tap(pos)
+ return
+ elif pos := self.find("recruit/begin_recruit", scope=begin_recruit_scope):
+ self.ticket_number = self.get_ticket_number()
+ if self.ticket_number == 0:
+ self.recruit_index = self.recruit_index + 1
+ logger.debug(f"{self.recruit_index} 张招募券")
+ return
+ self.tap(pos)
+ return
+
+ elif scene == Scene.RECRUIT_TAGS:
+ self.ticket_number = self.get_ticket_number()
return self.recruit_tags()
- elif self.scene() == Scene.SKIP:
- self.tap_element('skip')
- elif self.scene() == Scene.RECRUIT_AGENT:
+ elif scene == Scene.REFRESH_TAGS:
+ self.tap_element("recruit/refresh_comfirm")
+ elif scene == Scene.RECRUIT_AGENT:
return self.recruit_result()
- elif self.scene() == Scene.MATERIEL:
- self.tap_element('materiel_ico')
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_recruit')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
+ elif scene == Scene.SKIP:
+ self.tap_element("skip")
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
else:
- raise RecognizeError('Unknown scene')
-
- def recruit_tags(self) -> bool:
- """ 识别公招标签的逻辑 """
- if self.find('recruit_no_ticket') is not None:
- self.has_ticket = False
- if self.find('recruit_no_refresh') is not None:
- self.can_refresh = False
-
- needs = self.find('career_needs', judge=False)
- avail_level = self.find('available_level', judge=False)
- budget = self.find('recruit_budget', judge=False)
- up = needs[0][1] - 80
- down = needs[1][1] + 60
- left = needs[1][0]
- right = avail_level[0][0]
-
- while True:
- # ocr the recruitment tags and rectify
- img = self.recog.img[up:down, left:right]
- ocr = ocrhandle.predict(img)
- for x in ocr:
- if x[1] not in recruit_tag:
- x[1] = ocr_rectify(img, x, recruit_tag, '公招标签')
-
- # recruitment tags
- tags = [x[1] for x in ocr]
- logger.info(f'公招标签:{tags}')
-
- # choose tags
- choose, best = self.tags_choose(tags, self.priority)
- if best.choose < (1 << 5) and best.min <= 3:
- # refresh
- if self.tap_element('recruit_refresh', detected=True):
- self.tap_element('double_confirm', 0.8,
- interval=3, judge=False)
- continue
- elif not self.has_ticket and best.choose < (1 << 5) and best.min <= 4:
- # refresh, when no ticket
- if self.tap_element('recruit_refresh', detected=True):
- self.tap_element('double_confirm', 0.8,
- interval=3, judge=False)
- continue
- break
+ self.scene_graph_navigation(Scene.RECRUIT_MAIN)
+
+ def recruit_result(self):
+ try:
+ # 存在读完一次没退完再读一次
+ if str(self.recruit_index) in self.result_agent.keys():
+ self.tap((950, 150))
+ return
+
+ job_pt = None
+ for i in job_list:
+ if job_pt := self.find(i):
+ break
- # 如果没有招募券则只刷新标签不选人
- if not self.has_ticket:
- logger.debug('OK')
+ img = cropimg(self.recog.gray, ((job_pt[1][0], 730), (1800, 860)))
+ img = cv2.threshold(img, 220, 255, cv2.THRESH_BINARY)[1]
+
+ score = {}
+ for id in recruit_res_template:
+ res = recruit_res_template[id]
+ result = cv2.matchTemplate(img, res, cv2.TM_CCORR_NORMED, res)
+ _, max_val, _, _ = cv2.minMaxLoc(result)
+ score[id] = max_val
+ self.result_agent[self.recruit_index] = recruit_agent[
+ max(score, key=score.get)
+ ]["name"]
+
+ except Exception as e:
+ logger.exception(f"公招开包异常:{e}")
+ finally:
+ self.tap((500, 500))
+
+ def recruit_tags(self):
+ tags = self.get_recruit_tag()
+ logger.debug("tag识别结果{}".format(tags))
+
+ tem_res = self.recruit_cal(sorted(tags))
+ recruit_cal_result = None
+ recruit_result_level = -1
+
+ for index in self.recruit_order:
+ if tem_res[index]:
+ recruit_result_level = index
+ break
+ if recruit_result_level == -1:
+ logger.error("筛选结果为 {}".format(tem_res))
+ raise ValueError("筛选tag失败")
+ elif recruit_result_level != 3 and self.all_same_res(
+ tem_res, recruit_result_level
+ ):
+ recruit_cal_result = [tem_res[recruit_result_level][-1]]
+ else:
+ recruit_cal_result = tem_res[recruit_result_level]
+ logger.debug(recruit_cal_result)
+ if recruit_result_level == 3:
+ if pos := self.find("recruit/refresh"):
+ self.tap(pos)
+ return
+
+ if self.recruit_order.index(recruit_result_level) <= self.recruit_order_index:
+ logger.info("稀有tag,发送邮件")
+ send_message(
+ recruit_rarity.render(
+ recruit_results=recruit_cal_result,
+ title_text="稀有tag通知",
+ ),
+ "出稀有标签辣",
+ "WARNING",
+ )
+ if recruit_result_level == 6 or recruit_result_level == 1:
+ logger.debug(f"{recruit_result_level}星稀有tag ,不选")
+ self.recruit_index = self.recruit_index + 1
+ self.back()
+ return
+ elif recruit_result_level == 5:
+ if config.conf.recruit_auto_5 == 2:
+ if config.conf.recruit_auto_only5 and len(recruit_cal_result) > 1:
+ logger.debug(
+ f"{recruit_result_level}星稀有tag,但不止一个或纯手动选择"
+ )
+ self.recruit_index = self.recruit_index + 1
+ self.back()
+ return
+
+ if self.ticket_number == 0:
+ self.recruit_index = self.recruit_index + 1
self.back()
return
- # tap selected tags
- logger.info(f'选择:{choose}')
- for x in ocr:
- color = self.recog.img[up+x[2][0][1]-5, left+x[2][0][0]-5]
- if (color[2] < 100) != (x[1] not in choose):
- self.device.tap((left+x[2][0][0]-5, up+x[2][0][1]-5))
+ if (
+ self.ticket_number < config.conf.recruitment_permit
+ and recruit_result_level == 3
+ ):
+ self.recruit_index = self.recruit_index + 1
+ logger.info("没券 返回")
+ self.back()
+ return
+
+ choose = []
+ if recruit_result_level > 3:
+ choose = recruit_cal_result[0]["tag"]
- if best.choose < (1 << 5):
+ # tap selected tags
+ logger.info(f"选择标签:{list(choose)} ")
+ for x in choose:
+ # 存在choose为空但是进行标签选择的情况
+ logger.debug(f"tap{x}:{tags[x]}")
+ self.tap(tags[x])
+
+ # 9h为True 3h50min为False
+ logger.debug("开始选择时长")
+ # 默认三星招募时长是9:00
+ recruit_time_choose = 540
+ # 默认一星招募时长是3:50
+ if recruit_result_level == 1:
+ recruit_time_choose = 230
+
+ if recruit_time_choose == 540:
# 09:00
- self.tap_element('one_hour', 0.2, 0.8, 0)
- else:
+ logger.debug("时间9h")
+ self.tap_element("one_hour", 0.2, 0.8, 0.5)
+ elif recruit_time_choose == 230:
# 03:50
- [self.tap_element('one_hour', 0.2, 0.2, 0) for _ in range(2)]
- [self.tap_element('one_hour', 0.5, 0.2, 0) for _ in range(5)]
+ logger.debug("时间3h50min")
+ [self.tap_element("one_hour", 0.2, 0.2, 0.5) for _ in range(2)]
+ [self.tap_element("one_hour", 0.5, 0.2, 0.5) for _ in range(5)]
+ # elif recruit_time_choose == 460:
+ # # 07:40
+ # logger.debug("时间7h40min")
+ # [self.tap_element("one_hour", 0.2, 0.8, 0) for _ in range(2)]
+ # [self.tap_element("one_hour", 0.5, 0.8, 0) for _ in range(2)]
# start recruit
- self.tap((avail_level[1][0], budget[0][1]), interval=3)
-
- def recruit_result(self) -> bool:
- """ 识别公招招募到的干员 """
- agent = None
- ocr = ocrhandle.predict(self.recog.img)
- for x in ocr:
- if x[1][-3:] == '的信物':
- agent = x[1][:-3]
- agent_ocr = x
- break
- if agent is None:
- logger.warning('未能识别到干员名称')
+ self.tap_element("recruit/start_recruit")
+ self.ticket_number = self.ticket_number - 1
+
+ if recruit_result_level > 3:
+ self.agent_choose[str(self.recruit_index)] = {
+ "tags": list(choose),
+ "result": list(recruit_cal_result[0]["result"]),
+ }
+ tmp_res_list = []
+ for i in list(recruit_cal_result[0]["result"]):
+ tmp_res_list.append(i["name"])
+ logger.info(
+ "第{}个位置上的公招预测结果:{}".format(
+ self.recruit_index,
+ tmp_res_list,
+ )
+ )
else:
- if agent not in recruit_agent.keys():
- agent_with_suf = [x+'的信物' for x in recruit_agent.keys()]
- agent = ocr_rectify(
- self.recog.img, agent_ocr, agent_with_suf, '干员名称')[:-3]
- if agent in recruit_agent.keys():
- if 2 <= recruit_agent[agent]['stars'] <= 4:
- logger.info(f'获得干员:{agent}')
- else:
- logger.critical(f'获得干员:{agent}')
- self.tap((self.recog.w // 2, self.recog.h // 2))
-
- def tags_choose(self, tags: list[str], priority: list[str]) -> tuple[list[str], RecruitPoss]:
- """ 公招标签选择核心逻辑 """
- if priority is None:
- priority = []
- if len(priority) and isinstance(priority[0], str):
- priority = [[x] for x in priority]
- possibility: dict[int, RecruitPoss] = {}
- agent_level_dict = {}
-
- # 挨个干员判断可能性
- for x in recruit_agent.values():
- agent_name = x['name']
- agent_level = x['stars']
- agent_tags = x['tags']
- agent_level_dict[agent_name] = agent_level
-
- # 高级资深干员需要有特定的 tag
- if agent_level == 6 and '高级资深干员' not in tags:
+ self.agent_choose[str(self.recruit_index)] = {
+ "tags": list(choose),
+ "result": [{"id": "", "name": "随机三星干员", "star": 3}],
+ }
+ logger.info(
+ f'第{self.recruit_index}个位置上的公招预测结果:{"随机三星干员"}'
+ )
+ self.recruit_index = self.recruit_index + 1
+ return
+
+ def all_same_res(self, recruit_cal_res, index):
+ tmp_list = recruit_cal_res[index]
+ last_res = tmp_list[-1]
+ for i in range(len(tmp_list) - 2, -1, -1):
+ if tmp_list[i]["result"] != last_res["result"]:
+ return False
+ return True
+
+ def recruit_cal(self, tags: list[str]):
+ logger.debug(f"选择标签{tags}")
+ index_dict = {k: i for i, k in enumerate(self.recruit_order)}
+ combined_agent = {}
+ if "新手" in tags:
+ tags.remove("新手")
+ for item in combinations(tags, 1):
+ tmp = agent_with_tags[item[0]]
+
+ if len(tmp) == 0:
continue
+ tmp.sort(key=lambda k: k["star"], reverse=True)
+ combined_agent[item] = tmp
+ for item in combinations(tags, 2):
+ tmp = [j for j in agent_with_tags[item[0]] if j in agent_with_tags[item[1]]]
- # 统计 9 小时公招的可能性
- valid_9 = None
- if 3 <= agent_level <= 6:
- valid_9 = 0
- if agent_level == 6 and '高级资深干员' in tags:
- valid_9 |= (1 << tags.index('高级资深干员'))
- if agent_level == 5 and '资深干员' in tags:
- valid_9 |= (1 << tags.index('资深干员'))
- for tag in agent_tags:
- if tag in tags:
- valid_9 |= (1 << tags.index(tag))
-
- # 统计 3 小时公招的可能性
- valid_3 = None
- if 1 <= agent_level <= 4:
- valid_3 = 0
- for tag in agent_tags:
- if tag in tags:
- valid_3 |= (1 << tags.index(tag))
-
- # 枚举所有可能的标签组合子集
- for o in range(1 << 5):
- if valid_9 is not None and o & valid_9 == o:
- if o not in possibility.keys():
- possibility[o] = RecruitPoss(o)
- possibility[o].ls.append(agent_name)
- possibility[o].max = max(possibility[o].max, agent_level)
- possibility[o].min = min(possibility[o].min, agent_level)
- possibility[o].lv2a3 |= 2 <= agent_level <= 3
- _o = o + (1 << 5)
- if valid_3 is not None and o & valid_3 == o:
- if _o not in possibility.keys():
- possibility[_o] = RecruitPoss(_o)
- possibility[_o].ls.append(agent_name)
- possibility[_o].max = max(possibility[_o].max, agent_level)
- possibility[_o].min = min(possibility[_o].min, agent_level)
- possibility[_o].lv2a3 |= 2 <= agent_level <= 3
-
- # 检查是否存在无法从公开招募中获得的干员
- for considering in priority:
- for x in considering:
- if agent_level_dict.get(x) is None:
- logger.error(f'该干员并不能在公开招募中获得:{x}')
- raise RuntimeError
-
- best = RecruitPoss(0)
-
- # 按照优先级判断,必定选中同一星级干员
- # 附加限制:min_level == agent_level
- if best.poss == 0:
- logger.debug('choose: priority, min_level == agent_level')
- for considering in priority:
- for o in possibility.keys():
- possibility[o].poss = 0
- for x in considering:
- if x in possibility[o].ls:
- agent_level = agent_level_dict[x]
- if agent_level != 1 and agent_level == possibility[o].min:
- possibility[o].poss += 1
- elif agent_level == 1 and agent_level == possibility[o].min == possibility[o].max:
- # 必定选中一星干员的特殊逻辑
- possibility[o].poss += 1
- possibility[o].poss /= len(possibility[o].ls)
- if best < possibility[o]:
- best = possibility[o]
- if best.poss > 0:
- break
-
- # 按照优先级判断,若目标干员 1 星且该组合不存在 2/3 星的可能,则选择
- # 附加限制:min_level == agent_level == 1 and not lv2a3
- if best.poss == 0:
- logger.debug(
- 'choose: priority, min_level == agent_level == 1 and not lv2a3')
- for considering in priority:
- for o in possibility.keys():
- possibility[o].poss = 0
- for x in considering:
- if x in possibility[o].ls:
- agent_level = agent_level_dict[x]
- if agent_level == possibility[o].min == 1 and not possibility[o].lv2a3:
- # 特殊判断:选中一星和四星干员的 Tag 组合
- possibility[o].poss += 1
- possibility[o].poss /= len(possibility[o].ls)
- if best < possibility[o]:
- best = possibility[o]
- if best.poss > 0:
- break
-
- # 按照优先级判断,必定选中星级 >= 4 的干员
- # 附加限制:min_level >= 4
- if best.poss == 0:
- logger.debug('choose: priority, min_level >= 4')
- for considering in priority:
- for o in possibility.keys():
- possibility[o].poss = 0
- if possibility[o].min >= 4:
- for x in considering:
- if x in possibility[o].ls:
- possibility[o].poss += 1
- possibility[o].poss /= len(possibility[o].ls)
- if best < possibility[o]:
- best = possibility[o]
- if best.poss > 0:
- break
-
- # 按照等级下限判断,必定选中星级 >= 4 的干员
- # 附加限制:min_level >= 4
- if best.poss == 0:
- logger.debug('choose: min_level >= 4')
- for o in possibility.keys():
- possibility[o].poss = 0
- if possibility[o].min >= 4:
- possibility[o].poss = possibility[o].min
- if best < possibility[o]:
- best = possibility[o]
-
- # 按照优先级判断,检查其概率
- if best.poss == 0:
- logger.debug('choose: priority')
- for considering in priority:
- for o in possibility.keys():
- possibility[o].poss = 0
- for x in considering:
- if x in possibility[o].ls:
- possibility[o].poss += 1
- possibility[o].poss /= len(possibility[o].ls)
- if best < possibility[o]:
- best = possibility[o]
- if best.poss > 0:
- break
-
- # 按照等级下限判断,默认高稀有度优先
- if best.poss == 0:
- logger.debug('choose: min_level')
- for o in possibility.keys():
- possibility[o].poss = possibility[o].min
- if best < possibility[o]:
- best = possibility[o]
-
- logger.debug(f'poss: {possibility}')
- logger.debug(f'best: {best}')
-
- # 返回选择的标签列表
- choose = []
- for i in range(len(tags)):
- if best.choose & (1 << i):
- choose.append(tags[i])
- return choose, best
+ if len(tmp) == 0:
+ continue
+ tmp.sort(key=lambda k: k["star"])
+ combined_agent[item] = tmp
+ for item in combinations(tags, 3):
+ tmp1 = [
+ j for j in agent_with_tags[item[0]] if j in agent_with_tags[item[1]]
+ ]
+ tmp = [j for j in tmp1 if j in agent_with_tags[item[2]]]
+
+ if len(tmp) == 0:
+ continue
+ tmp.sort(key=lambda k: k["star"], reverse=True)
+ combined_agent[item] = tmp
+
+ sorted_list = sorted(
+ combined_agent.items(), key=lambda x: index_dict[x[1][0]["star"]]
+ )
+
+ result_dict = {}
+ for item in sorted_list:
+ result_dict[item[0]] = []
+ max_star = -1
+ min_star = 7
+ for agent in item[1]:
+ if "高级资深干员" not in item[0] and agent["star"] == 6:
+ continue
+ if agent["star"] > max_star:
+ max_star = agent["star"]
+ if agent["star"] < min_star:
+ min_star = agent["star"]
+ for agent in item[1]:
+ if max_star > 1 and agent["star"] == 2:
+ continue
+ if max_star > 1 and agent["star"] == 1:
+ continue
+ if max_star < 6 and agent["star"] == 6:
+ continue
+ result_dict[item[0]].append(agent)
+ logger.debug(item[0], agent)
+ try:
+ for key in list(result_dict.keys()):
+ if len(result_dict[key]) == 0:
+ result_dict.pop(key)
+
+ result_dict[item[0]] = sorted(
+ result_dict[item[0]], key=lambda x: x["star"], reverse=True
+ )
+ min_star = result_dict[item[0]][-1]["star"]
+ for res in result_dict[item[0]][:]:
+ if res["star"] > min_star:
+ result_dict[item[0]].remove(res)
+ except KeyError:
+ logger.debug("Recruit Cal Key Error :{}".format(result_dict))
+ continue
+ result = {
+ 6: [],
+ 5: [],
+ 4: [],
+ 3: [],
+ 2: [],
+ 1: [],
+ }
+ for tag in result_dict:
+ result[result_dict[tag][0]["star"]].append(
+ {"tag": tag, "result": result_dict[tag]}
+ )
+ for item in result:
+ if result[item]:
+ logger.debug("{}:{}".format(item, result[item]))
+ return result
+
+ def get_recruit_tag(self):
+ up = 520
+ down = 740
+ left = 530
+ right = 1300
+
+ img = self.recog.img[up:down, left:right]
+ tags_img = self.split_tags(img)
+ tags = {}
+ h, w, _ = img.shape
+
+ for index, value in enumerate(tags_img):
+ max_v = -1
+ tag_res = None
+ for key in tag_template:
+ res = cv2.matchTemplate(
+ value,
+ tag_template[key],
+ cv2.TM_CCORR_NORMED,
+ )
+
+ _, max_val, _, _ = cv2.minMaxLoc(res)
+ if max_val > max_v:
+ tag_res = {"tag": key, "val": max_val}
+ max_v = max_val
+ tag_pos = (
+ int(left + (index % 3) * int(w / 3) + 30),
+ int(up + int(index / 3) * int(h / 2) + 30),
+ )
+ tags[tag_res["tag"]] = tag_pos
+ return tags
+
+ def split_tags(self, img):
+ tag_img = []
+ h, w, _ = img.shape
+ ori_img = img
+ tag_h, tag_w = int(h / 2), int(w / 3)
+ for i in range(0, 2):
+ for j in range(0, 3):
+ if i * j == 2:
+ continue
+ tag_img.append(img[0:tag_h, 0:tag_w])
+ img = img[0:h, tag_w:w]
+ img = ori_img[tag_h:h, 0:w]
+
+ return tag_img
+
+ def get_ticket_number(self, height: int | None = 0, thres: int | None = 180):
+ p1, p2 = self.find("recruit/ticket")
+ p3, _ = self.find("recruit/stone")
+ p1 = (p2[0], p1[1] + 10)
+ p3 = (p3[0] - 30, p2[1] - 5)
+ img = cropimg(self.recog.gray, (p1, p3))
+ default_height = 29
+ if height and height != default_height:
+ scale = default_height / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, thres)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+ value = 0
+ for x, y, w, h in rect:
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ if digit.size < 800:
+ continue
+ score = []
+ for i in range(10):
+ im = number[i]
+ if digit.shape[0] < im.shape[0] or digit.shape[1] < im.shape[1]:
+ continue
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, _, _, _ = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+ return value
+
+ def add_recruit_param(self):
+ if not config.conf.recruit_robot:
+ self.recruit_order = [6, 5, 4, 3, 2, 1]
+ self.recruit_order_index = 1
diff --git a/arknights_mower/solvers/report.py b/arknights_mower/solvers/report.py
new file mode 100644
index 000000000..4b47e2b02
--- /dev/null
+++ b/arknights_mower/solvers/report.py
@@ -0,0 +1,214 @@
+import datetime
+import os
+
+import cv2
+import pandas as pd
+
+from arknights_mower.models import noto_sans
+from arknights_mower.utils.device.device import Device
+from arknights_mower.utils.digit_reader import DigitReader
+from arknights_mower.utils.email import report_template, send_message
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+from arknights_mower.utils.recognize import Recognizer, Scene, tp
+
+
+def remove_blank(target: str):
+ if target is None or target == "":
+ return target
+
+ target.strip()
+ target.replace(" ", "")
+ target.replace("\u3000", "")
+ return target
+
+
+class ReportSolver(SceneGraphSolver):
+ def __init__(
+ self,
+ device: Device = None,
+ recog: Recognizer = None,
+ ) -> None:
+ super().__init__(device, recog)
+ self.record_path = get_path("@app/tmp/report.csv")
+ self.low_range_gray = (100, 100, 100)
+ self.high_range_gray = (255, 255, 255)
+ self.date = (
+ (datetime.datetime.now() - datetime.timedelta(hours=4)).date().__str__()
+ )
+ self.digitReader = DigitReader()
+ self.report_res = {
+ "作战录像": None,
+ "赤金": None,
+ "龙门币订单": None,
+ "龙门币订单数": None,
+ "合成玉": None,
+ "合成玉订单数量": None,
+ }
+ self.reload_time = 0
+
+ def run(self):
+ if self.has_record():
+ logger.info("今天的基报看过了")
+ return True
+ logger.info("康康大基报捏~")
+ try:
+ super().run()
+ return True
+ except Exception as e:
+ logger.exception(e)
+ return False
+
+ def transition(self) -> bool:
+ if (scene := self.scene()) == Scene.RIIC_REPORT:
+ return self.read_report()
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ else:
+ self.scene_graph_navigation(Scene.RIIC_REPORT)
+
+ def read_report(self):
+ if self.find("riic/manufacture"):
+ try:
+ self.crop_report()
+ logger.info(self.report_res)
+ self.record_report()
+ except Exception as e:
+ logger.exception("基报读取失败:{}".format(e))
+ return True
+ else:
+ if self.reload_time > 3:
+ logger.info("未加载出基报")
+ return True
+ self.reload_time += 1
+ self.sleep(1)
+ return
+
+ def record_report(self):
+ logger.info(f"存入{self.date}的数据{self.report_res}")
+ try:
+ res_df = pd.DataFrame(self.report_res, index=[self.date])
+ res_df.to_csv(
+ self.record_path,
+ mode="a",
+ header=not os.path.exists(self.record_path),
+ encoding="gbk",
+ )
+ except Exception as e:
+ logger.exception(f"存入数据失败:{e}")
+ self.tap((1253, 81), interval=2)
+ try:
+ send_message(
+ report_template.render(
+ report_data=self.report_res, title_text="基建报告"
+ ),
+ "基建报告",
+ "INFO",
+ attach_image=self.recog.img,
+ )
+ except Exception as e:
+ logger.exception(f"基报邮件发送失败:{e}")
+ self.tap((40, 80), interval=2)
+
+ def has_record(self):
+ try:
+ if os.path.exists(self.record_path) is False:
+ logger.debug("基报不存在")
+ return False
+ df = pd.read_csv(self.record_path, encoding="gbk", on_bad_lines="skip")
+ for item in df.iloc:
+ if item[0] == self.date:
+ return True
+ return False
+ except PermissionError:
+ logger.info("report.csv正在被占用")
+ except pd.errors.EmptyDataError:
+ return False
+
+ def crop_report(self):
+ exp_area = [[1625, 200], [1800, 230]]
+ iron_pos = self.find("riic/iron")
+ iron_area = [
+ [iron_pos[1][0], iron_pos[0][1]],
+ [1800, iron_pos[1][1]],
+ ]
+ trade_pt = self.find("riic/trade")
+ assist_pt = self.find("riic/assistants")
+ area = {
+ "iron_order": [[1620, trade_pt[1][1] + 10], [1740, assist_pt[0][1] - 50]],
+ "iron_order_number": [
+ [1820, trade_pt[1][1] + 10],
+ [1870, assist_pt[0][1] - 65],
+ ],
+ "orundum": [[1620, trade_pt[1][1] + 45], [1870, assist_pt[0][1]]],
+ "orundum_number": [
+ [1820, trade_pt[1][1] + 55],
+ [1860, assist_pt[0][1] - 20],
+ ],
+ }
+
+ img = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ img = cv2.inRange(img, (98, 0, 150), (102, 255, 255))
+ self.report_res["作战录像"] = self.get_number(img, exp_area, height=19)
+ self.report_res["赤金"] = self.get_number(img, iron_area, height=19)
+ self.report_res["龙门币订单"] = self.get_number(
+ img, area["iron_order"], height=19
+ )
+ self.report_res["合成玉"] = self.get_number(img, area["orundum"], height=19)
+ logger.info("蓝字读取完成")
+
+ img = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ img = cv2.inRange(img, (0, 0, 50), (100, 100, 170))
+ self.report_res["龙门币订单数"] = self.get_number(
+ img, area["iron_order_number"], height=19, thres=200
+ )
+ self.report_res["合成玉订单数量"] = self.get_number(
+ img, area["orundum_number"], height=19, thres=200
+ )
+ logger.info("订单数读取完成")
+
+ def get_number(
+ self, img, scope: tp.Scope, height: int | None = 18, thres: int | None = 100
+ ):
+ img = cropimg(img, scope)
+
+ default_height = 29
+ if height and height != default_height:
+ scale = default_height / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, thres)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+ value = 0
+ for x, y, w, h in rect:
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+
+ score = []
+ for i in range(10):
+ im = noto_sans[i]
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+
+ return value
+
+
+def get_report_data():
+ record_path = get_path("@app/tmp/report.csv")
+ try:
+ data = {}
+ if os.path.exists(record_path) is False:
+ logger.debug("基报不存在")
+ return False
+ df = pd.read_csv(record_path, encoding="gbk")
+ data = df.to_dict("dict")
+ print(data)
+ except PermissionError:
+ logger.info("report.csv正在被占用")
diff --git a/arknights_mower/solvers/schedule.py b/arknights_mower/solvers/schedule.py
deleted file mode 100644
index 6c0b3044c..000000000
--- a/arknights_mower/solvers/schedule.py
+++ /dev/null
@@ -1,259 +0,0 @@
-import datetime
-import time
-from collections.abc import Callable
-from functools import cmp_to_key
-from pathlib import Path
-
-from ruamel.yaml import yaml_object
-
-from ..utils import config
-from ..utils.datetime import the_same_day
-from ..utils.device import Device
-from ..utils.log import logger
-from ..utils.param import operation_times, parse_operation_params
-from ..utils.priority_queue import PriorityQueue
-from ..utils.recognize import Recognizer
-from ..utils.solver import BaseSolver
-from ..utils.typealias import ParamArgs
-from ..utils.yaml import yaml
-from .operation import OpeSolver
-
-task_priority = {'base': 0, 'recruit': 1, 'mail': 2,
- 'credit': 3, 'shop': 4, 'mission': 5, 'operation': 6}
-
-
-class ScheduleLogError(ValueError):
- """ Schedule log 文件解析错误 """
-
-
-def operation_one(args: ParamArgs = [], device: Device = None) -> bool:
- """
- 只为 schedule 模块使用的单次作战操作
- 目前不支持使用源石和体力药
-
- 返回值表示该次作战是否成功
- 完成剿灭不算成功
- """
- level, _, _, _, eliminate = parse_operation_params(args)
- remain_plan = OpeSolver(device).run(level, 1, 0, 0, eliminate)
- for plan in remain_plan:
- if plan[1] != 0:
- return False
- return True
-
-
-@yaml_object(yaml)
-class Task(object):
- """
- 单个任务
- """
-
- def __init__(self, tag: str = '', cmd: Callable = None, args: ParamArgs = [], device: Device = None):
- self.cmd = cmd
- self.cmd_args = args
- self.tag = tag
- self.last_run = None
- self.idx = None
- self.pending = False
- self.total = 1
- self.finish = 0
- self.device = device
-
- # per_hour 任务的第一次执行将在启动脚本后的一个小时之后
- if tag == 'per_hour':
- self.last_run = datetime.datetime.now()
- if cmd.__name__ == 'operation':
- self.total = operation_times(args)
- assert self.total != 0
-
- @classmethod
- def to_yaml(cls, representer, data):
- last_run = ''
- if data.last_run is not None:
- last_run = data.last_run.strftime('%Y-%m-%d %H:%M:%S')
- return representer.represent_mapping('task',
- {'tag': data.tag,
- 'cmd': data.cmd.__name__,
- 'cmd_args': data.cmd_args,
- 'last_run': last_run,
- 'idx': data.idx,
- 'pending': data.pending,
- 'total': data.total,
- 'finish': data.finish})
-
- def __lt__(self, other):
- if task_priority[self.cmd.__name__] != task_priority[other.cmd.__name__]:
- return task_priority[self.cmd.__name__] < task_priority[other.cmd.__name__]
- return self.idx < other.idx
-
- def load(self, last_run: str = '', idx: int = 0, pending: bool = False, total: int = 1, finish: int = 0):
- if last_run == '':
- self.last_run = None
- else:
- self.last_run = datetime.datetime.strptime(
- last_run, '%Y-%m-%d %H:%M:%S')
- self.idx = idx
- self.pending = pending
- self.total = total
- self.finish = finish
-
- def reset(self):
- if self.tag != 'per_hour':
- self.last_run = None
- self.pending = False
- self.finish = 0
-
- def set_idx(self, idx: int = None):
- self.idx = idx
-
- def start_up(self) -> bool:
- return self.tag == 'start_up'
-
- def need_run(self, now: datetime.datetime = datetime.datetime.now()) -> bool:
- if self.pending:
- return False
- if self.start_up():
- if self.last_run is not None:
- return False
- self.pending = True
- self.last_run = now
- return True
- if self.tag[:4] == 'day_':
- # 同一天 and 跑过了
- if self.last_run is not None and the_same_day(now, self.last_run):
- return False
- # 还没到时间
- if now.strftime('%H:%M') < self.tag.replace('_', ':')[4:]:
- return False
- self.pending = True
- self.last_run = now
- return True
- if self.tag == 'per_hour':
- if self.last_run + datetime.timedelta(hours=1) <= now:
- self.pending = True
- self.last_run = now
- return True
- return False
- return False
-
- def run(self) -> bool:
- logger.info(f'task: {self.cmd.__name__} {self.cmd_args}')
- if self.cmd.__name__ == 'operation':
- if operation_one(self.cmd_args, self.device):
- self.finish += 1
- if self.finish == self.total:
- self.finish = 0
- self.pending = False
- return True
- return False
- self.cmd(self.cmd_args, self.device)
- self.pending = False
- return True
-
-
-def cmp_for_init(task_a: Task = None, task_b: Task = None) -> int:
- if task_a.start_up() and task_b.start_up():
- return 0
-
- if task_a.start_up():
- return -1
-
- if task_b.start_up():
- return 1
- return 0
-
-
-@yaml_object(yaml)
-class ScheduleSolver(BaseSolver):
- """
- 按照计划定时、自动完成任务
- """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
- self.tasks = []
- self.pending_list = PriorityQueue()
- self.device = device
- self.last_run = None
- self.schedule_log_path = Path(
- config.LOGFILE_PATH).joinpath('schedule.log')
-
- @classmethod
- def to_yaml(cls, representer, data):
- return representer.represent_mapping('Schedule', {'last_run': data.last_run.strftime('%Y-%m-%d %H:%M:%S'),
- 'tasks': data.tasks})
-
- def dump_to_disk(self):
- with self.schedule_log_path.open('w', encoding='utf8') as f:
- yaml.dump(self, f)
- logger.info('计划已存档')
-
- def load_from_disk(self, cmd_list=None, matcher: Callable = None) -> bool:
- if cmd_list is None:
- cmd_list = []
- try:
- with self.schedule_log_path.open('r', encoding='utf8') as f:
- data = yaml.load(f)
- self.last_run = datetime.datetime.strptime(
- data['last_run'], '%Y-%m-%d %H:%M:%S')
- for task in data['tasks']:
- cmd = matcher(task['cmd'], cmd_list)
- if cmd is None:
- raise ScheduleLogError
- new_task = Task(
- task['tag'], cmd, task['cmd_args'], self.device
- )
- new_task.load(
- task['last_run'], task['idx'], task['pending'], task['total'], task['finish']
- )
- self.tasks.append(new_task)
- if new_task.pending:
- self.pending_list.push(new_task)
- except Exception:
- return False
- logger.info('发现中断的计划,将继续执行')
- return True
-
- def add_task(self, tag: str = '', cmd: Callable = None, args: ParamArgs = []):
- task = Task(tag, cmd, args, self.device)
- self.tasks.append(task)
-
- def per_run(self):
- """
- 这里是为了处理优先级相同的情况,对于优先级相同时,我们依次考虑:
- 1. start_up 优先执行
- 2. 按照配置文件的顺序决定先后顺序
-
- sort 是稳定排序,详见:
- https://docs.python.org/3/library/functions.html#sorted
- """
- self.tasks.sort(key=cmp_to_key(cmp_for_init))
- for idx, task in enumerate(self.tasks):
- task.set_idx(idx)
-
- def run(self):
- logger.info('Start: 计划')
-
- super().run()
-
- def new_day(self):
- for task in self.tasks:
- task.reset()
- self.pending_list = PriorityQueue()
-
- def transition(self) -> None:
- while True:
- now = datetime.datetime.now()
- if self.last_run is not None and the_same_day(self.last_run, now) is False:
- self.new_day()
- self.last_run = now
- for task in self.tasks:
- if task.need_run(now):
- self.pending_list.push(task)
-
- task = self.pending_list.pop()
- if task is not None and task.run() is False:
- self.pending_list.push(task)
-
- self.dump_to_disk()
- time.sleep(60)
diff --git a/arknights_mower/solvers/secret_front.py b/arknights_mower/solvers/secret_front.py
new file mode 100644
index 000000000..6fd2ce14f
--- /dev/null
+++ b/arknights_mower/solvers/secret_front.py
@@ -0,0 +1,448 @@
+from datetime import datetime, timedelta
+from typing import Optional
+
+import cv2
+
+from arknights_mower.models import secret_front
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.email import send_message
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.matcher import Matcher
+from arknights_mower.utils.recognize import Scene
+from arknights_mower.utils.solver import BaseSolver
+
+
+def exp(card):
+ data = card[:3]
+ p = card[3]
+ return [i * p for i in data]
+
+
+def va(a, b):
+ return [a[0] + b[0], a[1] + b[1]]
+
+
+def sa(scope, vector):
+ return [va(scope[0], vector), va(scope[1], vector)]
+
+
+class SecretFront(BaseSolver):
+ target = {
+ "1A": [20, 20, 20],
+ "2A": [60, 55, 45],
+ "2B": [45, 55, 65],
+ "3A": [200, 155, 155],
+ "3B": [150, 220, 250],
+ "3C": [155, 255, 240],
+ "结局A": [510, 350, 350],
+ "结局B": [440, 440, 350],
+ "结局C": [350, 550, 350],
+ "结局D": [365, 475, 495],
+ "结局E": [370, 385, 620],
+ }
+ routes = {
+ "结局A": ["1A", "2A", "3A", "结局A"],
+ "结局B": ["1A", "2A", "3A", "结局B"],
+ "结局C": ["1A", "2A", "3B", "结局C"],
+ "结局D": ["1A", "2B", "3C", "结局D"],
+ "结局E": ["1A", "2B", "3C", "结局E"],
+ }
+ teams = {
+ "结局A": "management",
+ "结局B": "management",
+ "结局C": "intelligence",
+ "结局D": "medicine",
+ "结局E": "medicine",
+ }
+
+ @property
+ def route(self):
+ return self.routes[config.conf.secret_front.target]
+
+ @property
+ def team(self):
+ return self.teams[config.conf.secret_front.target]
+
+ def run(
+ self,
+ duration: Optional[timedelta] = None,
+ ):
+ logger.info("Start: 隐秘战线")
+
+ self.timeout = timedelta(seconds=config.conf.reclamation_algorithm.timeout)
+ self.deadline = datetime.now() + duration - self.timeout if duration else None
+ self.unknown_time = None
+
+ self.properties = None
+ self.route_matcher = None
+
+ self.event = False # 支援作战平台、游侠、诡影迷踪
+
+ self.reset_actions()
+
+ super().run()
+
+ def reset_actions(self):
+ self.actions = {}
+ for page in range(3):
+ self.actions[page] = {}
+
+ def number(self, scope: tp.Scope, height: Optional[int] = None):
+ img = cropimg(self.recog.gray, scope)
+ if height:
+ scale = 25 / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, 127)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+
+ value = 0
+
+ for x, y, w, h in rect:
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ score = []
+ for i in range(10):
+ im = secret_front[i]
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+
+ return value
+
+ def card_pos(self, total, idx):
+ if total == 3:
+ return [(301, 466), (830, 466), (1360, 466)][idx]
+ elif total == 2:
+ return [(565, 466), (1095, 466)][idx]
+ else:
+ return (830, 466)
+
+ def stage_card(self, total, idx):
+ pos = self.card_pos(total, idx)
+
+ scope = sa(((10, 380), (140, 430)), pos)
+ img = cropimg(self.recog.gray, scope)
+ img = cv2.copyMakeBorder(img, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,))
+ score = []
+ for i in self.target:
+ result = cv2.matchTemplate(img, secret_front[i], cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ name = list(self.target)[score.index(min(score))]
+
+ x, y = va(pos, (350, 460))
+ hsv = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ hue = hsv[y][x][0]
+
+ logger.debug(f"{name=} {hue=}")
+ return name, hue > 18
+
+ def card(self, total, idx):
+ pos = self.card_pos(total, idx)
+
+ materiel = sa(((84, 70), (180, 102)), pos)
+ intelligence = sa(((249, 70), (343, 102)), pos)
+ medicine = sa(((414, 70), (470, 102)), pos)
+ percentage = sa([[10, 440], [130, 473]], pos)
+
+ materiel = self.number(materiel)
+ intelligence = self.number(intelligence)
+ medicine = self.number(medicine)
+
+ min_val, min_loc = self.template_match(
+ "sf/percentage", percentage, cv2.TM_SQDIFF_NORMED
+ )
+ percentage[1][0] = min_loc[0][0]
+ percentage = self.number(percentage, 28) / 100
+
+ logger.debug(f"{materiel=} {intelligence=} {medicine=} {percentage=}")
+
+ return materiel, intelligence, medicine, percentage
+
+ def page_number(self):
+ title = cropimg(self.recog.gray, ((1020, 230), (1210, 280)))
+ if self.route_matcher is None:
+ return None
+ pos = self.route_matcher.match(title)
+ if not pos:
+ return None
+ pos_x = pos[0][0]
+ if pos_x < 800:
+ page_number = 0
+ elif pos_x < 1330:
+ page_number = 1
+ else:
+ page_number = 2
+ logger.debug(f"{page_number=}")
+ return page_number
+
+ def card_total(self):
+ p3 = self.card_pos(3, 0)
+ p2 = self.card_pos(2, 0)
+ up_scope = ((0, 0), (473, 120))
+ down_scope = ((0, 432), (150, 474))
+ s3u = sa(up_scope, p3)
+ s3d = sa(down_scope, p3)
+ s2u = sa(up_scope, p2)
+ s2d = sa(down_scope, p2)
+ if (pos := self.find("sf/card", scope=s3u)) and pos[0][0] < 350:
+ total = 3
+ elif self.find("sf/available", scope=s3d):
+ total = 3
+ elif (pos := self.find("sf/card", scope=s2u)) and pos[0][0] < 610:
+ total = 2
+ elif self.find("sf/available", scope=s2d):
+ total = 2
+ else:
+ total = 1
+ logger.debug(f"{total=}")
+ return total
+
+ def max_card(self):
+ max_page = -1
+ max_card = -1
+ max_score = -1
+
+ logger.debug(f"{self.properties=}")
+
+ # 根据目标计算
+ for page, page_action in self.actions.items():
+ for card, action in page_action.items():
+ for stage in self.route:
+ target = self.target[stage]
+ if all(p < t for p, t in zip(self.properties, target)):
+ break
+ distance = [max(t - p, 0) for p, t in zip(self.properties, target)]
+ if sum(distance) == 0:
+ return 0, 0
+ total_score = 0
+ for i in range(3):
+ score = action[i] * distance[i] / sum(distance)
+ total_score += score
+ logger.debug(f"{page=} {card=} {stage=} {total_score=}")
+ if total_score > max_score:
+ max_score = total_score
+ max_page = page
+ max_card = card
+ logger.debug(f"{max_page=} {max_card=}")
+ return max_page, max_card
+
+ def choose_card(self, total, idx):
+ self.route_matcher = None
+ self.properties = None
+ self.reset_actions()
+
+ if total == 3:
+ start = 545
+ elif total == 2:
+ start = 805
+ else:
+ start = 1075
+ self.tap((start + idx * 530, 900), interval=0.5)
+ self.tap((start + idx * 530, 900), interval=2)
+
+ def move_forward(self, scene):
+ # 从首页进入隐秘战线
+ if scene == Scene.INDEX:
+ self.tap_index_element("terminal")
+ elif scene == Scene.TERMINAL_MAIN:
+ self.tap_terminal_button("main_theme")
+ elif scene == Scene.TERMINAL_MAIN_THEME:
+ self.tap_element("navigation/main/14")
+ elif scene == Scene.SF_ENTRANCE:
+ self.tap_element("sf/entrance")
+
+ # 选择小队
+ elif scene == Scene.SF_SELECT_TEAM:
+ self.tap_element(f"sf/team_{self.team}")
+ self.tap_element("sf/select_team_ok")
+
+ # 继续前进
+ elif scene == Scene.SF_CONTINUE:
+ self.tap_element("sf/continue")
+
+ # 选择路线时识别已有属性值
+ elif scene == Scene.SF_SELECT:
+ self.event = False
+
+ if self.properties is None:
+ self.properties = [
+ self.number(((105, 490), (180, 525)), 23),
+ self.number(((105, 580), (180, 615)), 23),
+ self.number(((105, 675), (180, 705)), 23),
+ ]
+ logger.debug(f"{self.properties=}")
+
+ if self.route_matcher is None:
+ self.route_matcher = Matcher(self.recog.gray)
+
+ self.series = None
+ if (
+ (pos := self.find("sf/support_battle_platform"))
+ or (pos := self.find("sf/ranger"))
+ or (
+ self.route[-1] == "结局E"
+ and (pos := self.find("sf/lost_in_the_trick"))
+ )
+ ):
+ if (pos_x := pos[0][0]) < 800:
+ self.series = 0
+ elif pos_x < 1330:
+ self.series = 1
+ else:
+ self.series = 2
+ logger.debug(f"{self.series=}")
+
+ self.tap((545 + 530 * self.series, 640), interval=1.5)
+ return
+
+ self.tap((545, 640), interval=1.5)
+
+ # 行动列表
+ elif scene == Scene.SF_ACTIONS:
+ total = self.card_total()
+
+ if self.event:
+ name_list = [self.stage_card(total, i) for i in range(total)]
+ for idx, data in enumerate(name_list):
+ name, available = data
+ if name in self.route:
+ if available:
+ self.choose_card(total, idx)
+ else:
+ self.exit = "restart"
+ self.tap_element("sf/exit_button")
+ return
+ self.exit = "restart"
+ self.tap_element("sf/exit_button")
+ return
+
+ if (page_number := self.page_number()) is None:
+ self.sleep()
+ return
+
+ target = self.target[self.route[-1]]
+ distance = [max(t - p, 0) for p, t in zip(self.properties, target)]
+ if sum(distance) == 0:
+ self.choose_card(total, 0)
+ self.sleep(3)
+ return
+
+ if self.series is not None:
+ card_data = [self.card(total, idx) for idx in range(total)]
+ for idx in range(total):
+ self.actions[page_number][idx] = exp(card_data[idx])
+ if all(card[3] < 0.8 for card in card_data):
+ logger.debug("成功概率太低")
+ self.series = None
+ return
+
+ elif not all(self.actions.values()):
+ # 先看一遍所有行动,找到还没看过的页。
+ target_number = next(i for i in range(3) if not self.actions[i])
+ if target_number == page_number:
+ # 如果要读的是当前页,读取并计算分数
+ for idx in range(total):
+ self.actions[page_number][idx] = exp(self.card(total, idx))
+ elif (page_number + 1) % 3 == target_number:
+ self.tap((1785, 225)) # 下一页
+ else:
+ self.tap((350, 225)) # 上一页
+ return
+
+ max_page, max_card = self.max_card()
+
+ if max_page == page_number:
+ self.choose_card(len(self.actions[max_page]), max_card)
+ self.sleep(3)
+ elif (page_number + 1) % 3 == max_page:
+ self.tap((1785, 225)) # 下一页
+ else:
+ self.tap((350, 225)) # 上一页
+
+ # 行动结果
+ elif scene == Scene.SF_RESULT:
+ if pos := self.find("sf/continue_result"):
+ self.tap(pos)
+ else:
+ self.sleep()
+
+ elif scene == Scene.SF_EVENT:
+ self.event = True
+ self.tap_element("sf/continue_event", interval=1.5)
+
+ elif scene in [Scene.SF_TEAM_PASS, Scene.SF_CLICK_ANYWHERE, Scene.SF_END]:
+ self.tap((960, 980), interval=2)
+
+ if scene == Scene.SF_END:
+ send_message(
+ f"隐秘战线成功完成{config.conf.secret_front.target}", level="INFO"
+ )
+
+ # 关闭说明
+ elif scene == Scene.SF_INFO:
+ self.tap_element("sf/info", x_rate=0.17, y_rate=0.46)
+
+ elif scene == Scene.SF_EXIT:
+ if self.exit == "restart":
+ self.properties = None
+ self.route_matcher = None
+ self.event = False
+
+ self.tap_element("sf/restart")
+ self.tap_element("sf/confirm")
+ elif self.exit == "exit":
+ self.tap_element("sf/confirm")
+ else:
+ self.tap((480, 590))
+
+ else:
+ self.sleep()
+
+ def back_to_index(self, scene):
+ if scene in [Scene.TERMINAL_MAIN, Scene.TERMINAL_MAIN_THEME, Scene.SF_ENTRANCE]:
+ self.back()
+ elif scene == Scene.SF_EXIT:
+ self.move_forward(scene)
+ else:
+ self.exit = "exit"
+ self.tap_element("sf/exit_button")
+
+ def transition(self):
+ now = datetime.now()
+
+ if (scene := self.sf_scene()) == Scene.UNKNOWN:
+ if not self.unknown_time:
+ self.unknown_time = now
+ elif now - self.unknown_time > self.timeout:
+ logger.warning("连续识别到未知场景")
+ try:
+ self.properties = None
+ self.route_matcher = None
+ self.event = False
+ self.reset_actions()
+ super().back_to_index()
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ self.device.exit()
+ self.check_current_focus()
+ else:
+ self.unknown_time = None
+
+ if self.deadline and self.deadline < datetime.now():
+ if scene == Scene.INDEX:
+ return True
+ else:
+ self.back_to_index(scene)
+ else:
+ self.move_forward(scene)
diff --git a/arknights_mower/solvers/shop.py b/arknights_mower/solvers/shop.py
index e4eb65e49..991e8ac53 100644
--- a/arknights_mower/solvers/shop.py
+++ b/arknights_mower/solvers/shop.py
@@ -1,93 +1,165 @@
-from __future__ import annotations
+import cv2
-from ..data import shop_items
-from ..ocr import ocr_rectify, ocrhandle
-from ..utils import segment
-from ..utils.device import Device
-from ..utils.image import scope2slice
-from ..utils.log import logger
-from ..utils.recognize import RecognizeError, Scene
-from ..utils.solver import BaseSolver, Recognizer
+from arknights_mower.models import noto_sans, riic_base_digits, shop
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.graph import SceneGraphSolver
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.scene import Scene
+from arknights_mower.utils.vector import sa, va
+card_w, card_h = 352, 354
+top, left = 222, 25
+gap = 28
-class ShopSolver(BaseSolver):
- """
- 自动使用信用点购买物资
- """
+card_list = []
+for i in range(2):
+ for j in range(5):
+ card_list.append((left + j * (card_w + gap), top + i * (card_h + gap)))
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
- super().__init__(device, recog)
- def run(self, priority: list[str] = None) -> None:
- """
- :param priority: list[str], 使用信用点购买东西的优先级, 若无指定则默认购买第一件可购买的物品
- """
- self.priority = priority
- self.buying = None
- logger.info('Start: 商店')
- logger.info('购买期望:%s' % priority if priority else '无,购买到信用点用完为止')
+class CreditShop(SceneGraphSolver):
+ def run(self):
+ logger.info("Start: 信用商店购物")
super().run()
- def transition(self) -> bool:
- if self.scene() == Scene.INDEX:
- self.tap_element('index_shop')
- elif self.scene() == Scene.SHOP_OTHERS:
- self.tap_element('shop_credit_2')
- elif self.scene() == Scene.SHOP_CREDIT:
- collect = self.find('shop_collect')
- if collect is not None:
- self.tap(collect)
- else:
- return self.shop_credit()
- elif self.scene() == Scene.SHOP_CREDIT_CONFIRM:
- if self.find('shop_credit_not_enough') is None:
- self.tap_element('shop_cart')
- elif len(self.priority) > 0:
- # 移除优先级中买不起的物品
- self.priority.remove(self.buying)
- logger.info('信用点不足,放弃购买%s,看看别的...' % self.buying)
- self.back()
- else:
- return True
- elif self.scene() == Scene.SHOP_ASSIST:
- self.back()
- elif self.scene() == Scene.MATERIEL:
- self.tap_element('materiel_ico')
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.get_navigation():
- self.tap_element('nav_shop')
- elif self.scene() != Scene.UNKNOWN:
- self.back_to_index()
+ def number(
+ self,
+ scope: tp.Scope,
+ font: str = "noto",
+ height: int | None = None,
+ thres: int | None = 127,
+ ):
+ """基于模板匹配的数字识别
+
+ Args:
+ scope: 识别区域
+ font: 数字字体
+ height: 高度
+ """
+ img = cropimg(self.recog.gray, scope)
+
+ if font == "riic_base":
+ templates = riic_base_digits
+ default_height = 28
else:
- raise RecognizeError('Unknown scene')
-
- def shop_credit(self) -> bool:
- """ 购买物品逻辑 """
- segments = segment.credit(self.recog.img)
- valid = []
- for seg in segments:
- if self.find('shop_sold', scope=seg) is None:
- scope = (seg[0], (seg[1][0], seg[0][1] + (seg[1][1]-seg[0][1])//4))
- img = self.recog.img[scope2slice(scope)]
- ocr = ocrhandle.predict(img)
- if len(ocr) == 0:
- raise RecognizeError
- ocr = ocr[0]
- if ocr[1] not in shop_items:
- ocr[1] = ocr_rectify(img, ocr, shop_items, '物品名称')
- valid.append((seg, ocr[1]))
- logger.info(f'商店内可购买的物品:{[x[1] for x in valid]}')
- if len(valid) == 0:
+ templates = noto_sans
+ default_height = 29
+
+ if height and height != default_height:
+ scale = default_height / height
+ img = cv2.resize(img, None, None, scale, scale)
+ img = thres2(img, thres)
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ rect.sort(key=lambda c: c[0])
+
+ value = 0
+
+ for x, y, w, h in rect:
+ digit = cropimg(img, ((x, y), (x + w, y + h)))
+ digit = cv2.copyMakeBorder(
+ digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ score = []
+ for i in range(10):
+ im = templates[i]
+ result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ score.append(min_val)
+ value = value * 10 + score.index(min(score))
+
+ return value
+
+ def credit_remain(self):
+ credits = self.number(((1700, 39), (1800, 75)), "riic_base", thres=180)
+ logger.debug(f"{credits=}")
+ return credits
+
+ def product_info(self):
+ self.products = []
+ for idx in range(10):
+ pos = card_list[idx]
+ x, y = va(pos, (30, 30))
+ if self.recog.gray[y][x] > 110:
+ self.products.append(None)
+ continue
+ touch_scope = (pos, va(pos, (card_w, card_h)))
+ x, y = va(pos, (6, 60))
+ discount = 0
+ if self.recog.gray[y][x] < 150:
+ discount_scope = sa(((27, 65), (66, 95)), pos)
+ discount = self.number(discount_scope, "riic_base")
+ price_scope = sa(((15, 300), (340, 333)), pos)
+ price = self.number(price_scope, thres=180)
+
+ score = 1
+ item_name = None
+ name_scope = sa(((60, 11), (320, 45)), pos)
+ target = cropimg(self.recog.gray, name_scope)
+ target = cv2.copyMakeBorder(
+ target, 10, 10, 30, 10, cv2.BORDER_CONSTANT, None, (0,)
+ )
+ target = thres2(target, 127)
+ for name, img in shop.items():
+ result = cv2.matchTemplate(target, img, cv2.TM_SQDIFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if min_val < score:
+ score = min_val
+ item_name = name
+ if item_name == "技巧概要":
+ if self.number(sa(((252, 14), (270, 40)), pos)) == 1:
+ item_name += "·卷1"
+ else:
+ item_name += "·卷2"
+
+ self.products.append(
+ {
+ "name": item_name,
+ "touch": touch_scope,
+ "discount": discount,
+ "price": price,
+ }
+ )
+ logger.debug(self.products)
+
+ def transition(self):
+ if (scene := self.scene()) == Scene.SHOP_CREDIT:
+ if pos := self.find("shop_collect"):
+ self.tap(pos)
+ return
+ remain = self.credit_remain()
+ self.product_info()
+ for product in self.products:
+ if (
+ product
+ and product["name"] in config.conf.maa_mall_buy
+ and remain > product["price"]
+ ):
+ self.tap(product["touch"])
+ return
+ for product in self.products:
+ if (
+ product
+ and product["name"] not in config.conf.maa_mall_blacklist
+ and remain > product["price"]
+ ):
+ self.tap(product["touch"])
+ return
+ if remain > 300 and config.conf.maa_mall_ignore_blacklist_when_full:
+ for product in self.products:
+ if product and remain > product["price"]:
+ self.tap(product["touch"])
+ return
return True
- priority = self.priority
- if priority is not None:
- valid.sort(
- key=lambda x: 9999 if x[1] not in priority else priority.index(x[1]))
- if valid[0][1] not in priority:
+ elif scene == Scene.SHOP_CREDIT_CONFIRM:
+ if self.find("shop_credit_not_enough"):
+ self.back()
return True
- logger.info(f'实际购买顺序:{[x[1] for x in valid]}')
- self.buying = valid[0][1]
- self.tap(valid[0][0], interval=3)
+ else:
+ self.tap_element("shop_cart")
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ else:
+ self.scene_graph_navigation(Scene.SHOP_CREDIT)
diff --git a/arknights_mower/solvers/skland.py b/arknights_mower/solvers/skland.py
new file mode 100644
index 000000000..36b791d26
--- /dev/null
+++ b/arknights_mower/solvers/skland.py
@@ -0,0 +1,242 @@
+import datetime
+import hashlib
+import hmac
+import json
+import os
+import time
+from urllib import parse
+
+import pandas as pd
+import requests
+
+from arknights_mower.utils import config
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+app_code = "4ca99fa6b56cc2ba"
+
+# 签到url
+sign_url = "https://zonai.skland.com/api/v1/game/attendance"
+# 绑定的角色url
+binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
+# 验证码url
+login_code_url = "https://as.hypergryph.com/general/v1/send_phone_code"
+# 验证码登录
+token_phone_code_url = "https://as.hypergryph.com/user/auth/v2/token_by_phone_code"
+# 密码登录
+token_password_url = "https://as.hypergryph.com/user/auth/v1/token_by_phone_password"
+# 使用token获得认证代码
+grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
+# 使用认证代码获得cred
+cred_code_url = "https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code"
+
+
+class SKLand:
+ def __init__(self):
+ self.record_path = get_path("@app/tmp/skland.csv")
+
+ self.header = {
+ "cred": "",
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+ self.header_login = {
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+
+ self.reward = []
+ # 签名请求头一定要这个顺序,否则失败
+ # timestamp是必填的,其它三个随便填,不要为none即可
+ self.header_for_sign = {"platform": "", "timestamp": "", "dId": "", "vName": ""}
+ self.sign_token = ""
+ self.all_recorded = True
+
+ def start(self):
+ for item in config.conf.skland_info:
+ if self.has_record(item.account):
+ continue
+ self.all_recorded = False
+ self.save_param(self.get_cred_by_token(self.log(item)))
+ for i in self.get_binding_list():
+ body = {"gameId": 1, "uid": i.get("uid")}
+ # list_awards(1, i.get('uid'))
+ resp = requests.post(
+ sign_url,
+ headers=self.get_sign_header(sign_url, "post", body, self.header),
+ json=body,
+ ).json()
+ if resp["code"] != 0:
+ self.reward.append(
+ {"nickName": item.account, "reward": resp.get("message")}
+ )
+ logger.info(f'{i.get("nickName")}:{resp.get("message")}')
+ continue
+ awards = resp["data"]["awards"]
+ for j in awards:
+ res = j["resource"]
+ self.reward.append(
+ {
+ "nickName": item.account,
+ "reward": "{}×{}".format(res["name"], j.get("count") or 1),
+ }
+ )
+ logger.info(
+ f'{i.get("nickName")}获得了{res["name"]}×{j.get("count") or 1}'
+ )
+ if len(self.reward) > 0:
+ return self.record_log()
+ if self.all_recorded:
+ return True
+ return False
+
+ def save_param(self, cred_resp):
+ self.header["cred"] = cred_resp["cred"]
+ self.sign_token = cred_resp["token"]
+
+ def log(self, account):
+ r = requests.post(
+ token_password_url,
+ json={"phone": account.account, "password": account.password},
+ headers=self.header_login,
+ ).json()
+ if r.get("status") != 0:
+ raise Exception(f'获得token失败:{r["msg"]}')
+ return r["data"]["token"]
+
+ def get_cred_by_token(self, token):
+ return self.get_cred(self.get_grant_code(token))
+
+ def get_grant_code(self, token):
+ response = requests.post(
+ grant_code_url,
+ json={"appCode": app_code, "token": token, "type": 0},
+ headers=self.header_login,
+ )
+ resp = response.json()
+ if response.status_code != 200:
+ raise Exception(f"获得认证代码失败:{resp}")
+ if resp.get("status") != 0:
+ raise Exception(f'获得认证代码失败:{resp["msg"]}')
+ return resp["data"]["code"]
+
+ def get_cred(self, grant):
+ resp = requests.post(
+ cred_code_url, json={"code": grant, "kind": 1}, headers=self.header_login
+ ).json()
+ if resp["code"] != 0:
+ raise Exception(f'获得cred失败:{resp["message"]}')
+ return resp["data"]
+
+ def get_binding_list(self):
+ v = []
+ resp = requests.get(
+ binding_url,
+ headers=self.get_sign_header(binding_url, "get", None, self.header),
+ ).json()
+
+ if resp["code"] != 0:
+ print(f"请求角色列表出现问题:{resp['message']}")
+ if resp.get("message") == "用户未登录":
+ print("用户登录可能失效了,请重新运行此程序!")
+ return []
+ for i in resp["data"]["list"]:
+ if i.get("appCode") != "arknights":
+ continue
+ v.extend(i.get("bindingList"))
+ return v
+
+ def get_sign_header(self, url: str, method, body, old_header):
+ h = json.loads(json.dumps(old_header))
+ p = parse.urlparse(url)
+ if method.lower() == "get":
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, p.query
+ )
+ else:
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, json.dumps(body)
+ )
+ for i in header_ca:
+ h[i] = header_ca[i]
+ return h
+
+ def generate_signature(self, token: str, path, body_or_query):
+ """
+ 获得签名头
+ 接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dId,platform,timestamp,vName).toJSON()
+ 将此字符串做HMAC加密,算法为SHA-256,密钥token为请求cred接口会返回的一个token值
+ 再将加密后的字符串做MD5即得到sign
+ :param token: 拿cred时候的token
+ :param path: 请求路径(不包括网址)
+ :param body_or_query: 如果是GET,则是它的query。POST则为它的body
+ :return: 计算完毕的sign
+ """
+ # 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2
+
+ t = str(int(time.time()) - 2)
+ token = token.encode("utf-8")
+ header_ca = json.loads(json.dumps(self.header_for_sign))
+ header_ca["timestamp"] = t
+ header_ca_str = json.dumps(header_ca, separators=(",", ":"))
+ s = path + body_or_query + t + header_ca_str
+ hex_s = hmac.new(token, s.encode("utf-8"), hashlib.sha256).hexdigest()
+ md5 = (
+ hashlib.md5(hex_s.encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")
+ .decode("utf-8")
+ )
+ return md5, header_ca
+
+ def record_log(self):
+ date_str = datetime.datetime.now().strftime("%Y/%m/%d")
+ logger.info(f"存入{date_str}的数据{self.reward}")
+ try:
+ for item in self.reward:
+ res_df = pd.DataFrame(item, index=[date_str])
+ res_df.to_csv(self.record_path, mode="a", header=False, encoding="gbk")
+ except Exception as e:
+ logger.exception(e)
+
+ return True
+
+ def has_record(self, phone: str):
+ try:
+ if os.path.exists(self.record_path) is False:
+ logger.debug("无森空岛记录")
+ return False
+ df = pd.read_csv(
+ self.record_path, header=None, encoding="gbk", on_bad_lines="skip"
+ )
+ for item in df.iloc:
+ if item[0] == datetime.datetime.now().strftime("%Y/%m/%d"):
+ if item[1].astype(str) == phone:
+ logger.info(f"{phone}今天签到过了")
+ return True
+ return False
+ except PermissionError:
+ logger.info("skland.csv正在被占用")
+ except pd.errors.EmptyDataError:
+ return False
+
+ def test_connect(self):
+ res = []
+ for item in config.conf.skland_info:
+ if item.isCheck:
+ try:
+ self.save_param(self.get_cred_by_token(self.log(item)))
+ for i in self.get_binding_list():
+ if i["uid"]:
+ res.append(
+ "{}连接成功".format(
+ i["nickName"] + "({})".format(i["channelName"])
+ )
+ )
+ except Exception as e:
+ msg = "{}无法连接-{}".format(item.account, e)
+ logger.exception(msg)
+ res.append(msg)
+ return res
diff --git a/arknights_mower/strategy.py b/arknights_mower/strategy.py
deleted file mode 100644
index c6c2194e2..000000000
--- a/arknights_mower/strategy.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from __future__ import annotations
-
-import functools
-
-from .solvers import *
-from .solvers.base_schedule import BaseSchedulerSolver
-from .utils import typealias as tp
-from .utils.device import Device
-from .utils.recognize import Recognizer
-from .utils.solver import BaseSolver
-
-
-class Solver(object):
- """ Integration solver """
-
- def __init__(self, device: Device = None, recog: Recognizer = None, timeout: int = 99) -> None:
- """
- :param timeout: int, 操作限时,单位为小时
- """
- self.device = device if device is not None else Device()
- self.recog = recog if recog is not None else Recognizer(self.device)
- self.timeout = timeout
-
-
- def base_scheduler (self,tasks=[],plan={},current_base={},)-> None:
- # 返还所有排班计划以及 当前基建干员位置
- return BaseSchedulerSolver(self.device, self.recog).run(tasks,plan,current_base)
-
- def base(self, arrange: tp.BasePlan = None, clue_collect: bool = False, drone_room: str = None, fia_room: str = None) -> None:
- """
- :param arrange: dict(room_name: [agent,...]), 基建干员安排
- :param clue_collect: bool, 是否收取线索
- :param drone_room: str, 是否使用无人机加速
- :param fia_room: str, 是否使用菲亚梅塔恢复心情
- """
- BaseSolver(self.device, self.recog).run(
- arrange, clue_collect, drone_room, fia_room)
-
- def credit(self) -> None:
- CreditSolver(self.device, self.recog).run()
-
- def mission(self) -> None:
- MissionSolver(self.device, self.recog).run()
-
- def recruit(self, priority: list[str] = None) -> None:
- """
- :param priority: list[str], 优先考虑的公招干员,默认为高稀有度优先
- """
- RecruitSolver(self.device, self.recog).run(priority)
-
- def ope(self, level: str = None, times: int = -1, potion: int = 0, originite: int = 0, eliminate: int = 0, plan: list[tp.OpePlan] = None) -> list[tp.OpePlan]:
- """
- :param level: str, 指定关卡,默认为前往上一次关卡或当前界面关卡
- :param times: int, 作战的次数上限,-1 为无限制,默认为 -1
- :param potion: int, 使用药剂恢复体力的次数上限,-1 为无限制,默认为 0
- :param originite: int, 使用源石恢复体力的次数上限,-1 为无限制,默认为 0
- :param eliminate: int, 是否优先处理未完成的每周剿灭,0 为忽略剿灭,1 为优先剿灭,2 为优先剿灭但只消耗代理卡,默认为 0
- :param plan: [[str, int]...], 指定多个关卡以及次数,优先级高于 level
-
- :return remain_plan: [[str, int]...], 未完成的计划
- """
- return OpeSolver(self.device, self.recog).run(level, times, potion, originite, eliminate, plan)
-
- def shop(self, priority: bool = None) -> None:
- """
- :param priority: list[str], 使用信用点购买东西的优先级, 若无指定则默认购买第一件可购买的物品
- """
- ShopSolver(self.device, self.recog).run(priority)
-
- def mail(self) -> None:
- MailSolver(self.device, self.recog).run()
-
- def index(self) -> None:
- BaseSolver(self.device, self.recog).back_to_index()
diff --git a/arknights_mower/templates/README.md b/arknights_mower/templates/README.md
deleted file mode 100644
index 5c02f85a5..000000000
--- a/arknights_mower/templates/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Templates
-
-## config.yaml
-
-配置文件模板
diff --git a/arknights_mower/templates/config.yaml b/arknights_mower/templates/config.yaml
deleted file mode 100644
index 3d6e15c99..000000000
--- a/arknights_mower/templates/config.yaml
+++ /dev/null
@@ -1,356 +0,0 @@
----
-
-此文件为配置文件模板,更新时将覆盖。
-配置文件为 config.yaml,首次运行时自动生成。
-
----
-
-version: 1
-
-debug:
- # 是否启动调试模式
- enabled: true
- logfile:
- # 日志存放路径
- path: ./log
- # 日志数量
- amount: 3
- screenshot:
- # 截图存放路径
- path: ./screenshot
- # 单个文件夹内截图数量的上限
- max_total: 20
-
-device:
- # 需要启动 adb server 时使用的 adb 程序,为空时则尝试 PATH 中的 adb
- # 以下为部分模拟器提供的 adb 程序,不一定正确,仅供参考
- adb_binary:
- # - D:\leidian\LDPlayer\adb.exe # 雷电模拟器
- # - C:\Program Files\Nox\bin\nox_adb.exe # 夜神模拟器
- # - C:\Program Files\BlueStacks\HD-Adb.exe # BlueStacks
- # - C:\Program Files (x86)\MuMu\emulator\nemu\vmonitor\bin\adb_server.exe # MuMu 模拟器
-
- # 优先选择指定设备进行操作,指定 adb devices 中列出的名称
- # 若都无法连接则优先选择在线的设备进行连接
- adb_device:
- - 127.0.0.1:62001
-
- # 无设备连接时进行的连接操作
- adb_connect:
- - 127.0.0.1:5555 # 大部分模拟器
- - 127.0.0.1:7555 # MuMu模拟器
-
- # 连接 ADB Server 参数,默认无需修改
- # adb_server:
- # ip: 127.0.0.1
- # port: 5037
- # timeout: 5
-
- # 选择控制方式,,可选项为 [minitouch, scrcpy],默认选择 scrcpy
- # adb_control_client: scrcpy
- # adb_control_client: minitouch # Android 10 及以上不支持
-
- # 指定设备上的 Touch Device,若无则自动检测
- # mnt_touch_device: /dev/input/event4
-
- # minitouch 使用的转发端口
- # mnt_port: 20937
-
- # minitouch 兼容模式,当打开时, 用于处理在某些模拟器下无法正确处理分辨率和旋转的情况, 默认关闭
- # mnt_compatibility_mode: false
-
-account:
- # 账户以及密码
- # username: 15088888888
- # password: PassWord
-
-app:
- package_name: com.hypergryph.arknights # 官服
- # package_name: com.hypergryph.arknights.bilibili # Bilibili 服
- activity_name: com.u8.sdk.U8UnityContext
-
-ocr:
- # ocr.space 免费 API Token
- # 若不可用则需要自己申请一个 → https://ocr.space/OCRAPI
- ocr_space_api: c7431c9d7288957
-
-behavior:
- max_retry: 5
-
-schedule:
- # 开始运行时的计划任务
- start_up: &full_task
- - mail # 自动收取邮件
- - base -c # 自动收取基建并使用线索
- - credit # 自动收取信用点
- # 目前 schedule 模块中的作战暂不支持使用理智药和源石
- - operation -e # 自动执行上一个作战直到体力耗尽,优先处理未完成的每周剿灭
- # - operation AP-5 # 自动执行 AP-5 作战
- # - operation 1-7 99 # 重复刷 1-7 关卡 99 次
- - shop # 自动购买信用点商店的内容,购物的优先级以下文的 priority 部分作为标准
- - recruit # 自动进行公招,招募的优先级以下文的 priority 部分作为标准
- - mission # 自动完成每日/每周任务
-
- # 每个小时的计划任务
- per_hour:
- # - base -c -d33 # -c为自动收取基建并使用线索;-d33 为自动加速 B303 的建筑
- - recruit
-
- # 定时计划任务
- day_07_00: *full_task
- day_15_00: *full_task
- day_23_00: *full_task
-
- # 自动换班的相关任务,每天定时执行
- # day_08_00:
- # - base plan_1
- # day_14_00:
- # - base plan_2
- # day_20_00:
- # - base plan_3
- # day_02_00:
- # - base plan_4
-
-priority:
- # 公招优先级
- recruit:
- - [因陀罗, 火神]
- # - [Lancet-2, Castle-3, THRM-EX, 正义骑士号]
- # 商店优先级
- shop:
- - 招聘许可
- - 赤金
- - 龙门币
- - 初级作战记录
- - 技巧概要·卷2
-
-# 自定义基建排班
-# 这里自定义了一套排班策略,实现的是两班倒,分为四个阶段
-# 阶段 1 和 2 为第一班,阶段 3 和 4 为第二班
-# 第一班的干员在阶段 3 和 4 分两批休息,第二班同理
-# 每个阶段耗时 6 小时
-
-# 若菲亚梅塔出现在列表中,则会优先选择菲亚梅塔,以防止其与意料之外的干员交换心情
-
-arrangement:
- plan_1:
- # 控制中枢
- central:
- - 夕
- - 令
- - 凯尔希
- - 阿米娅
- - 玛恩纳
- # 办公室
- contact:
- - 艾雅法拉
- # 会客室
- meeting:
- - 陈
- - 红
- # 宿舍
- dormitory_1:
- - 杜林
- - 闪灵
- - 安比尔
- - 空弦
- - 缠丸
- dormitory_2:
- - 推进之王
- - 琴柳
- - 赫默
- - 杰西卡
- - 调香师
- dormitory_3:
- - 夜莺
- - 波登可
- - 夜刀
- - 古米
- - 空爆
- dormitory_4:
- - 空
- - Lancet-2
- - 香草
- - 史都华德
- - 刻俄柏
- # 制造站 + 贸易站 + 发电站
- room_1_1:
- - 德克萨斯
- - 能天使
- - 拉普兰德
- room_1_2:
- - 断罪者
- - 食铁兽
- - 槐琥
- room_1_3:
- - 阿消
- room_2_1:
- - 巫恋
- - 柏喙
- - 慕斯
- room_2_2:
- - 红豆
- - 霜叶
- - 白雪
- room_2_3:
- - 雷蛇
- room_3_1:
- - Castle-3
- - 梅尔
- - 白面鸮
- room_3_2:
- - 格雷伊
- room_3_3:
- - 砾
- - 夜烟
- - 斑点
- plan_2:
- dormitory_1:
- - 杜林
- - 闪灵
- - 芬
- - 稀音
- - 克洛丝
- dormitory_2:
- - 推进之王
- - 琴柳
- - 清流
- - 森蚺
- - 温蒂
- dormitory_3:
- - 夜莺
- - 波登可
- - 伊芙利特
- - 深靛
- - 炎熔
- dormitory_4:
- - 空
- - Lancet-2
- - 远山
- - 星极
- - 普罗旺斯
- plan_3:
- contact:
- - 普罗旺斯
- meeting:
- - 远山
- - 星极
- dormitory_1:
- - 杜林
- - 闪灵
- - 格雷伊
- - 雷蛇
- - 阿消
- dormitory_2:
- - 推进之王
- - 琴柳
- - 德克萨斯
- - 能天使
- - 拉普兰德
- dormitory_3:
- - 夜莺
- - 波登可
- - 巫恋
- - 柏喙
- - 慕斯
- dormitory_4:
- - 空
- - Lancet-2
- - 艾雅法拉
- - 陈
- - 红
- room_1_1:
- - 安比尔
- - 空弦
- - 缠丸
- room_1_2:
- - 赫默
- - 杰西卡
- - 调香师
- room_1_3:
- - 伊芙利特
- room_2_1:
- - 夜刀
- - 古米
- - 空爆
- room_2_2:
- - 香草
- - 史都华德
- - 刻俄柏
- room_2_3:
- - 深靛
- room_3_1:
- - 芬
- - 稀音
- - 克洛丝
- room_3_2:
- - 炎熔
- room_3_3:
- - 清流
- - 森蚺
- - 温蒂
- plan_4:
- dormitory_1:
- - 杜林
- - 闪灵
- - 断罪者
- - 食铁兽
- - 槐琥
- dormitory_2:
- - 推进之王
- - 琴柳
- - 红豆
- - 霜叶
- - 白雪
- dormitory_3:
- - 夜莺
- - 波登可
- - Castle-3
- - 梅尔
- - 白面鸮
- dormitory_4:
- - 空
- - Lancet-2
- - 砾
- - 夜烟
- - 斑点
- # 让心情最低的干员进宿舍恢复的计划
- plan_recover:
- dormitory_1:
- - Free
- - Free
- - Free
- - Free
- - Free
- dormitory_2:
- - Free
- - Free
- - Free
- - Free
- - Free
- dormitory_3:
- - Free
- - Free
- - Free
- - Free
- - Free
- dormitory_4:
- - Free
- - Free
- - Free
- - Free
- - Free
-
-# 作战
-operation:
- # 作战的次数上限,-1 为无限制,默认为 -1
- times: -1
- # 使用药剂恢复体力的次数上限,-1 为无限制,默认为 0
- potion: 0
- # 使用源石恢复体力的次数上限,-1 为无限制,默认为 0
- originite: 0
- # 是否优先处理未完成的每周剿灭,0 为忽略剿灭,1 为优先剿灭,2 为优先剿灭但只消耗代理卡,默认为 0
- eliminate: 0
- plan: # 指定多个关卡以及次数,两者用英文逗号分隔
- - 1-7,-1
- # - 3-1,3
diff --git a/arknights_mower/templates/maa.html b/arknights_mower/templates/maa.html
new file mode 100644
index 000000000..103f61293
--- /dev/null
+++ b/arknights_mower/templates/maa.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+ Maa刷图掉落
+
+
+
+
+
+ # |
+ 掉落物品 |
+
+ {% for drop in stage_drop.details %}
+
+ {{ loop.index }} |
+
+ {% for item in drop %}{{ item.itemName }}x{{ item.quantity }}{% if not
+ loop.last %},{% endif %}{% endfor %}
+ |
+
+ {% endfor %}
+
+ 合计 |
+
+ {% for item in stage_drop.summary %}{{ item.itemName }}x{{
+ item.quantity }}{% if not loop.last %},{% endif %}{% endfor %}
+ |
+
+
+
+
diff --git a/arknights_mower/templates/recruit_rarity.html b/arknights_mower/templates/recruit_rarity.html
new file mode 100644
index 000000000..cd303c718
--- /dev/null
+++ b/arknights_mower/templates/recruit_rarity.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ {{ title_text }}
+
+
+
+
+ 标签选择 | 预计干员名单 |
+
+ {% for result in recruit_results %}
+
+
+ {{ result['tag'] }}
+ |
+
+ {% for agent in result['result'] %}
+ {{ agent.name }}
+ {% endfor %}
+ |
+
+
+ {% endfor %}
+
+
+
+
diff --git a/arknights_mower/templates/recruit_template.html b/arknights_mower/templates/recruit_template.html
new file mode 100644
index 000000000..d47c67263
--- /dev/null
+++ b/arknights_mower/templates/recruit_template.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+ {{ title_text }}
+
+
+
+
+ {% if recruit_results|length > 0 %}
+
+ 位置 |
+ 标签 |
+ 预计干员 |
+
+ {% for pos,value in recruit_results.items() %}
+
+ {{ pos }} |
+
+
+ {% if value.tags|length > 0 %}
+ {% for tag in value.tags %}{{ tag }}{% endfor %}
+ {% else %}
+ 未选择
+ {% endif %}
+
+ |
+
+ {% for agent in value.result %}
+ {{ agent.name }}
+ {% endfor %}
+ |
+
+
+ {% endfor %} {% endif %} {% if recruit_get_agent|length > 0 %}
+
+ 上次公招干员 |
+
+ {% for pos,value in recruit_get_agent.items() %}
+
+ {{ pos }} |
+ {{ value }} |
+
+ {% endfor %} {% endif %}
+
+
+ {% if permit_count is defined%}
+ 剩余招募券数量 |
+ {{permit_count}} |
+ {% endif %}
+
+
+
+
+
+
diff --git a/arknights_mower/templates/report_template.html b/arknights_mower/templates/report_template.html
new file mode 100644
index 000000000..e8031285d
--- /dev/null
+++ b/arknights_mower/templates/report_template.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ 基建报告
+
+
+
+
+ 赤金 | {{ report_data['赤金'] }} |
+ 龙门币订单 | {{ report_data['龙门币订单'] }} |
+ 龙门币订单数 | {{ report_data['龙门币订单数'] }} |
+ 作战录像 | {{ report_data['作战录像'] }} |
+ 合成玉 | {{ report_data['合成玉'] }} |
+
+
+
diff --git a/arknights_mower/templates/task.html b/arknights_mower/templates/task.html
new file mode 100644
index 000000000..6beaf0e55
--- /dev/null
+++ b/arknights_mower/templates/task.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+ 任务
+
+
+
+
+
+ 时间 |
+ 任务 |
+
+ {% for task in tasks %}
+ {% if task.plan or task.type == '释放宿舍空位'%}
+ {% for r, p in task.plan.items() %}
+
+ {% if loop.first %}
+ {{ task.time.strftime('%H:%M:%S') }} |
+ {% endif %}
+ {% if task.type == '释放宿舍空位'%}
+ {{ task.meta_data }}在{{ base_scheduler.translate_room(r) }}休息完毕 |
+ {% else %}
+ {{ base_scheduler.translate_room(r) }} |
+ {% endif %}
+ {{ p|join(', ') }} |
+
+ {% endfor %}
+ {% elif task.type %}
+
+ {{ task.time.strftime('%H:%M:%S') }} |
+ {{ task.type }} |
+
+ {% else %}
+
+ {{ task.time.strftime('%H:%M:%S') }} |
+ 空任务 |
+
+ {% endif %}
+ {% endfor %}
+
+
+
diff --git a/arknights_mower/templates/version.html b/arknights_mower/templates/version.html
new file mode 100644
index 000000000..7a5cfdbea
--- /dev/null
+++ b/arknights_mower/templates/version.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 版本
+
+
+ 当前版本{{ current }}过旧,请及时更新。
+ 以下为推荐的mower版本:
+ 稳定版:
+
+ {% for i in stable %}
+ - {{ i }}
+ {% endfor %}
+
+ 测试版:
+
+ {% for i in testing %}
+ - {{ i }}
+ {% endfor %}
+
+
+
diff --git a/arknights_mower/tests/base_scheduler_tests.py b/arknights_mower/tests/base_scheduler_tests.py
new file mode 100644
index 000000000..f91c8f2fc
--- /dev/null
+++ b/arknights_mower/tests/base_scheduler_tests.py
@@ -0,0 +1,158 @@
+import unittest
+from datetime import datetime
+from unittest.mock import MagicMock, patch
+
+from arknights_mower.solvers.base_schedule import BaseSchedulerSolver
+from arknights_mower.utils.logic_expression import LogicExpression
+from arknights_mower.utils.plan import Plan, PlanConfig, Room
+
+with patch.dict("sys.modules", {"RecruitSolver": MagicMock()}):
+ pass
+
+
+class TestBaseScheduler(unittest.TestCase):
+ @patch.object(BaseSchedulerSolver, "__init__", lambda x: None)
+ def test_backup_plan_solver_Caper(self):
+ plan_config = {
+ "meeting": [
+ Room("伊内丝", "", ["见行者", "陈"]),
+ Room("跃跃", "", ["见行者", "陈"]),
+ ]
+ }
+ plan_config1 = {
+ "meeting": [
+ Room("伊内丝", "", ["陈", "红"]),
+ Room("见行者", "", ["陈", "红"]),
+ ]
+ }
+ agent_base_config = PlanConfig("稀音", "稀音", "伺夜")
+ plan = {
+ # 阶段 1
+ "default_plan": Plan(plan_config, agent_base_config),
+ "backup_plans": [
+ Plan(
+ plan_config1,
+ agent_base_config,
+ trigger=LogicExpression(
+ "op_data.party_time is None", "and", " True "
+ ),
+ task={"meeting": ["Current", "见行者"]},
+ )
+ ],
+ }
+
+ solver = BaseSchedulerSolver()
+ solver.global_plan = plan
+ solver.initialize_operators()
+ solver.tasks = []
+ with patch.object(BaseSchedulerSolver, "agent_get_mood") as mock_agent_get_mood:
+ mock_agent_get_mood.return_value = None
+ solver.backup_plan_solver()
+ self.assertEqual(len(solver.tasks), 1)
+ solver.party_time = datetime.now()
+ solver.backup_plan_solver()
+ self.assertTrue(
+ all(not condition for condition in solver.op_data.plan_condition)
+ )
+
+ @patch.object(BaseSchedulerSolver, "__init__", lambda x: None)
+ def test_backup_plan_solver_GreyytheLightningbearer(self):
+ plan_config = {
+ "room_2_3": [Room("雷蛇", "澄闪", ["炎狱炎熔", "格雷伊"])],
+ "room_1_3": [Room("承曦格雷伊", "自动化", ["炎狱炎熔"])],
+ "room_2_1": [
+ Room("温蒂", "自动化", ["泡泡"]),
+ Room("森蚺", "自动化", ["火神"]),
+ Room("清流", "自动化", ["贝娜"]),
+ ],
+ "room_2_2": [Room("澄闪", "澄闪", ["炎狱炎熔", "格雷伊"])],
+ "central": [
+ Room("阿米娅", "", ["诗怀雅"]),
+ Room("琴柳", "乌有", ["清道夫"]),
+ Room("重岳", "乌有", ["杜宾"]),
+ Room("夕", "乌有", ["玛恩纳"]),
+ Room("令", "乌有", ["凯尔希"]),
+ ],
+ "contact": [Room("桑葚", "乌有", ["絮雨"])],
+ }
+ backup_plan1_config = {
+ "central": [
+ Room("阿米娅", "", ["诗怀雅"]),
+ Room("清道夫", "", ["诗怀雅"]),
+ Room("杜宾", "", ["泡泡"]),
+ Room("玛恩纳", "", ["火神"]),
+ Room("森蚺", "", ["诗怀雅"]),
+ ],
+ "room_2_1": [
+ Room("温蒂", "", ["泡泡"]),
+ Room("掠风", "", ["贝娜"]),
+ Room("清流", "", ["火神"]),
+ ],
+ "room_1_3": [Room("Lancet-2", "", ["承曦格雷伊"])],
+ "room_2_2": [Room("澄闪", "", ["承曦格雷伊", "格雷伊"])],
+ "room_2_3": [Room("雷蛇", "", ["承曦格雷伊", "格雷伊"])],
+ "contact": [Room("絮雨", "", ["桑葚"])],
+ }
+ agent_base_config0 = PlanConfig(
+ "稀音,黑键,焰尾,伊内丝",
+ "稀音,柏喙,伊内丝",
+ "伺夜,帕拉斯,雷蛇,澄闪,红云,乌有,年,远牙,阿米娅,桑葚,截云,掠风",
+ ling_xi=2,
+ resting_threshold=0.1,
+ )
+ agent_base_config = PlanConfig(
+ "稀音,黑键,焰尾,伊内丝",
+ "稀音,柏喙,伊内丝",
+ "伺夜,帕拉斯,雷蛇,澄闪,红云,乌有,年,远牙,阿米娅,桑葚,截云",
+ ling_xi=2,
+ free_blacklist="艾丽妮,但书,龙舌兰",
+ )
+ plan = {
+ # 阶段 1
+ "default_plan": Plan(plan_config, agent_base_config),
+ "backup_plans": [
+ Plan(
+ backup_plan1_config,
+ agent_base_config0,
+ trigger=LogicExpression(
+ "op_data.operators['令'].current_room.startswith('dorm')",
+ "and",
+ LogicExpression(
+ "op_data.operators['温蒂'].current_mood() - op_data.operators['承曦格雷伊'].current_mood()",
+ ">",
+ "4",
+ ),
+ ),
+ task={
+ "dormitory_2": [
+ "Current",
+ "Current",
+ "Current",
+ "Current",
+ "承曦格雷伊",
+ ]
+ },
+ )
+ ],
+ }
+
+ solver = BaseSchedulerSolver()
+ solver.global_plan = plan
+ solver.initialize_operators()
+ solver.tasks = []
+ with patch.object(BaseSchedulerSolver, "agent_get_mood") as mock_agent_get_mood:
+ mock_agent_get_mood.return_value = None
+ solver.op_data.operators["令"].current_room = "dorm"
+ solver.op_data.operators["温蒂"].mood = 12
+ solver.op_data.operators["承曦格雷伊"].mood = 7
+ solver.backup_plan_solver()
+ self.assertEqual(len(solver.tasks), 1)
+ solver.op_data.operators["承曦格雷伊"].mood = 12
+ solver.backup_plan_solver()
+ self.assertTrue(
+ all(not condition for condition in solver.op_data.plan_condition)
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/arknights_mower/tests/cuoyu.py b/arknights_mower/tests/cuoyu.py
new file mode 100644
index 000000000..85d85cfd4
--- /dev/null
+++ b/arknights_mower/tests/cuoyu.py
@@ -0,0 +1,90 @@
+class Calculator:
+ def __init__(self):
+ self.input_日_钱_基建内_永远上班 = 50000
+ self.input_日_钱_基建内_暂时上班 = 30000
+ self.input_日_书_基建内 = 16500
+ self.input_预设土量 = 16000
+ self.input_搓玉人_效率和 = 2.1505
+ self.input_卖玉人_效率和 = 3
+ self.input_目标练度钱 = 761409
+ self.input_目标练度书 = 588286
+ self.目标练度钱书比 = self.input_目标练度钱 / self.input_目标练度书
+ self.练度列表 = []
+ self.练度列表_离谱 = []
+ self.日_钱_基建外 = 43482.63
+ self.日_书_基建外 = 36284.48
+
+ def 搓玉天数(self, 土量):
+ return int(土量 / 2 / 24 / self.input_搓玉人_效率和) + 1
+
+ def 搓玉用钱(self, 土量):
+ return 土量 * 800
+
+ def 卖玉天数(self, 土量):
+ return int(土量 / 2 / 2 / 24 * 2 / self.input_卖玉人_效率和) + 1
+
+ def calculate(self):
+ from itertools import product as 多循环
+
+ for 土量, 日_钱_基建内_永远上班, 日_书_基建内 in 多循环(
+ range(self.input_预设土量, 25000, 80),
+ range(self.input_日_钱_基建内_永远上班, 60000, 1000),
+ range(self.input_日_书_基建内, 20000, 1000),
+ ):
+ # 钱
+ 搓玉所需钱数 = self.搓玉用钱(土量)
+ 卖玉天数 = self.卖玉天数(土量)
+ 基建内_年产钱 = (
+ 日_钱_基建内_永远上班 * 365
+ + self.input_日_钱_基建内_暂时上班 * (365 - 卖玉天数)
+ )
+ 总_年产钱 = 基建内_年产钱 + self.日_钱_基建外 * 365 - 搓玉所需钱数
+ # 书
+ 搓玉天数 = self.搓玉天数(土量)
+ 总_年产书 = 日_书_基建内 * (365 - 搓玉天数) + self.日_书_基建外 * 365
+ 总_日产钱 = 总_年产钱 / 365
+ 总_日产书 = 总_年产书 / 365
+ # 最终计算
+ 日_钱书总数 = (
+ 日_钱_基建内_永远上班 + self.input_日_钱_基建内_暂时上班 + 日_书_基建内
+ )
+ 总_钱书比 = round(总_日产钱 / 总_日产书, 2)
+ 总_能练干员 = int(
+ min(总_年产钱 / self.input_目标练度钱, 总_年产书, self.input_目标练度书)
+ )
+ 日_钱_基建内 = int(日_钱_基建内_永远上班 + self.input_日_钱_基建内_暂时上班)
+ 日销售记录 = [
+ 日_钱_基建内,
+ 日_书_基建内,
+ 总_钱书比,
+ 总_能练干员,
+ 搓玉天数,
+ 卖玉天数,
+ 土量,
+ 土量 * 5,
+ ]
+ for i in 日销售记录:
+ if i < 0:
+ print("出错了")
+ self.练度列表_离谱.append(日销售记录)
+ if 日_钱书总数 < 100000 and abs(总_钱书比 - self.目标练度钱书比) < 0.005:
+ self.练度列表.append(日销售记录)
+
+ a = "日_钱_基建内, 日_书_基建内, 总_钱书比, 总_能练干员, 搓玉天数, 卖玉天数, 土量, 土量 * 5"
+ list2 = a.split(", ")
+
+ try:
+ self.练度列表排序 = sorted(self.练度列表, key=lambda x: x[-1])
+ for val1, val2 in zip(list2, self.练度列表排序[0]):
+ print(f"{val1}: {val2}")
+
+ except Exception:
+ print("出问题了")
+ 练度列表排序 = sorted(self.练度列表_离谱, key=lambda x: x[0])
+ for val1, val2 in zip(list2, 练度列表排序[0]):
+ print(f"{val1}: {val2}")
+
+
+# Create an instance of the Calculator class
+calc = Calculator()
+calc.calculate() # Perform calculations using the defined method
diff --git a/arknights_mower/tests/logic_expression_tests.py b/arknights_mower/tests/logic_expression_tests.py
new file mode 100644
index 000000000..85cdbcb61
--- /dev/null
+++ b/arknights_mower/tests/logic_expression_tests.py
@@ -0,0 +1,23 @@
+import unittest
+
+from arknights_mower.utils.logic_expression import get_logic_exp
+
+dict_minus = {"left": "114514", "operator": "-", "right": "1919810"}
+
+
+class TestLogicExpression(unittest.TestCase):
+ def test_single(self):
+ self.assertEqual(str(get_logic_exp({"left": "114514"})), "(114514 )")
+
+ def test_flat(self):
+ self.assertEqual(str(get_logic_exp(dict_minus)), "(114514 - 1919810)")
+
+ def test_nested(self):
+ self.assertEqual(
+ str(get_logic_exp({"left": "hi", "operator": "and", "right": dict_minus})),
+ "(hi and (114514 - 1919810))",
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/arknights_mower/tests/recruit_tests.py b/arknights_mower/tests/recruit_tests.py
new file mode 100644
index 000000000..8ac05b43d
--- /dev/null
+++ b/arknights_mower/tests/recruit_tests.py
@@ -0,0 +1,24 @@
+import unittest
+
+from arknights_mower.solvers.recruit import RecruitSolver
+
+
+class TestRecruitCal(unittest.TestCase):
+ @unittest.mock.patch.object(RecruitSolver, "__init__", lambda x: None)
+ def setUp(self):
+ self.test_class = RecruitSolver()
+ self.test_class.recruit_order = [6, 5, 1, 4, 3, 2]
+
+ def test_recruit_cal_with_order_1(self):
+ recruit_tags = ["重装干员", "先锋干员", "高级资深干员", "支援", "支援机械"]
+ results = self.test_class.recruit_cal(recruit_tags)
+ print(f"顺序为 {self.test_class.recruit_order}")
+ print(results.keys())
+ for i in results:
+ for result in results[i]:
+ for agent in result["result"]:
+ print(f"{i} {result['tag']} {agent['name']}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/arknights_mower/tests/scheduler_task_tests.py b/arknights_mower/tests/scheduler_task_tests.py
new file mode 100644
index 000000000..253bdcf8b
--- /dev/null
+++ b/arknights_mower/tests/scheduler_task_tests.py
@@ -0,0 +1,415 @@
+import unittest
+from datetime import datetime, timedelta
+from unittest.mock import MagicMock, patch
+
+from arknights_mower.utils.operators import Operators
+from arknights_mower.utils.plan import Plan, PlanConfig, Room
+from arknights_mower.utils.scheduler_task import (
+ SchedulerTask,
+ TaskTypes,
+ check_dorm_ordering,
+ find_next_task,
+ scheduling,
+)
+
+with patch.dict("sys.modules", {"save_action_to_sqlite_decorator": MagicMock()}):
+ pass
+
+
+class TestScheduling(unittest.TestCase):
+ def test_adjust_two_orders(self):
+ # 测试两个跑单任务被拉开
+ task1 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 1"},
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ task2 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:01", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 2"},
+ )
+ task3 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:02", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 3"},
+ )
+ task4 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:03", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 4"},
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ task5 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:30", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 5"},
+ )
+ tasks = [task1, task2, task3, task4, task5]
+ res = scheduling(
+ tasks, time_now=datetime.strptime("2023-09-19 09:01", "%Y-%m-%d %H:%M")
+ )
+ # 返还的是应该拉开跑单的任务
+ self.assertNotEqual(res, None)
+
+ def test_adjust_two_orders_fia(self):
+ # 测试菲亚换班时间预设3分钟有效
+ task1 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 1"},
+ )
+ task2 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:01", "%Y-%m-%d %H:%M"),
+ task_plan={},
+ task_type=TaskTypes.FIAMMETTA,
+ )
+ task4 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:03", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 4"},
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ task5 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:30", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 5"},
+ )
+ tasks = [task1, task2, task4, task5]
+ scheduling(
+ tasks, time_now=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M")
+ )
+ # 跑单任务被提前
+ self.assertEqual(tasks[0].type, TaskTypes.RUN_ORDER)
+
+ def test_adjust_time(self):
+ # 测试跑单任务被挤兑
+ task1 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 1"},
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ task2 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:01", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 2"},
+ )
+ task3 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:02", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 3"},
+ )
+ task4 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:03", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 4"},
+ task_type=TaskTypes.RUN_ORDER,
+ )
+ task5 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:30", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 5"},
+ )
+ tasks = [task1, task2, task3, task4, task5]
+ res = scheduling(
+ tasks, time_now=datetime.strptime("2023-09-19 10:01", "%Y-%m-%d %H:%M")
+ )
+ # 其他任务会被移送至跑单任务以后
+ self.assertEqual(tasks[2].plan["task"], "Task 4")
+ self.assertEqual(res, None)
+
+ def test_find_next(self):
+ # 测试 方程有效
+ task1 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 1"},
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="room",
+ )
+ task4 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:03", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 4"},
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="room",
+ )
+ task5 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:30", "%Y-%m-%d %H:%M"),
+ task_plan={"task": "Task 5"},
+ )
+ tasks = [task1, task4, task5]
+ now = datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M")
+ res1 = find_next_task(
+ tasks,
+ now + timedelta(minutes=5),
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="room",
+ compare_type=">",
+ )
+ res2 = find_next_task(
+ tasks,
+ now + timedelta(minutes=-60),
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="room",
+ compare_type=">",
+ )
+ self.assertEqual(res1, None)
+ self.assertNotEqual(res2, None)
+
+ def test_check_dorm_ordering_add_plan_1(self):
+ # 测试 方程有效
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "夕", "Current", "Current"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "Current"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ check_dorm_ordering(tasks, op_data)
+ # 生成额外宿舍任务
+ self.assertEqual(2, len(tasks))
+ # 验证第一个宿舍任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 老plan含有见行者
+ self.assertEqual("见行者", tasks[1].plan["dormitory_1"][3])
+ # 假设换班任务执行完毕
+ del tasks[0]
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(1, len(tasks))
+ # 验证第二个任务仅仅包宿舍任务
+ self.assertEqual(1, len(tasks[0].plan))
+
+ def test_check_dorm_ordering_add_plan_2(self):
+ # 测试 方程有效
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "夕", "Current", "Current"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "Current"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ # 预设干员位置
+ op_data.operators["见行者"].current_index = -1
+ op_data.operators["见行者"].current_room = "meeting"
+ op_data.operators["麒麟R夜刀"].current_index = 3
+ op_data.operators["麒麟R夜刀"].current_room = "dormitory_1"
+ check_dorm_ordering(tasks, op_data)
+ # 生成额外宿舍任务
+ self.assertEqual(2, len(tasks))
+ # 验证第一个宿舍任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 老plan不变
+ self.assertEqual("Free", tasks[1].plan["dormitory_1"][3])
+ # 假设换班任务执行完毕
+ del tasks[0]
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(1, len(tasks))
+ # 验证第二个任务仅仅包宿舍任务
+ self.assertEqual(1, len(tasks[0].plan))
+
+ def test_check_dorm_ordering_add_plan_3(self):
+ # 测试 宿舍4号位置已经吃到VIP的情况,安排新的高效干员去3号位置刷新VIP
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "Current", "夕", "Current"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "Current"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ # 预设干员位置
+ op_data.operators["红"].current_index = -1
+ op_data.operators["红"].current_room = "meeting"
+ op_data.operators["夕"].resting_priority = "low"
+ op_data.operators["焰尾"].current_index = 2
+ op_data.operators["焰尾"].current_room = "dormitory_1"
+ check_dorm_ordering(tasks, op_data)
+
+ # 如果非VIP位置被占用,则刷新
+ self.assertEqual(2, len(tasks))
+ # 验证第任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 假设换班任务执行完毕
+ del tasks[0]
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(1, len(tasks))
+ # 验证第任务包宿舍+换班任务
+ self.assertEqual(1, len(tasks[0].plan))
+
+ def test_check_dorm_ordering_add_plan_4(self):
+ # 测试 方程有效
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "夕", "Current", "Current"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "Current"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ op_data.operators["玛恩纳"].current_room = ""
+ op_data.operators["玛恩纳"].current_index = -1
+ check_dorm_ordering(tasks, op_data)
+ # 生成额外宿舍任务
+ self.assertEqual(2, len(tasks))
+ # 验证第一个宿舍任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 老plan含有见行者
+ self.assertEqual("见行者", tasks[1].plan["dormitory_1"][3])
+ # 假设换班任务执行完毕
+ del tasks[0]
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(1, len(tasks))
+ # 验证第二个任务仅仅包宿舍任务
+ self.assertEqual(1, len(tasks[0].plan))
+
+ def test_check_dorm_ordering_not_plan(self):
+ # 测试 如果当前已经有前置位VIP干员在吃单回,则不会新增任务
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "Current", "夕", "令"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "火龙S黑角"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ # 预设干员位置
+ op_data.operators["红"].current_index = -1
+ op_data.operators["夕"].resting_priority = "low"
+ op_data.operators["红"].current_room = "meeting"
+ op_data.operators["焰尾"].current_index = 2
+ op_data.operators["焰尾"].current_room = "dormitory_1"
+ check_dorm_ordering(tasks, op_data)
+
+ # 如果VIP位已经被占用,则不会生成新任务
+ self.assertEqual(1, len(tasks))
+ # 验证第任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(1, len(tasks))
+ # 验证第任务包宿舍+换班任务
+ self.assertEqual(2, len(tasks[0].plan))
+
+ def test_check_dorm_ordering_not_plan2(self):
+ # 测试 如果当前已经有前置位VIP干员在吃单回,则不会新增任务
+ task1 = SchedulerTask(
+ time=datetime.now(),
+ task_plan={
+ "dormitory_1": ["Current", "Current", "Current", "夕", "Current"],
+ "central": ["麒麟R夜刀", "Current", "Current", "Current", "Current"],
+ },
+ task_type=TaskTypes.SHIFT_OFF,
+ meta_data="",
+ )
+ tasks = [task1]
+ op_data = self.init_opdata()
+ # 预设干员位置
+ op_data.operators["红"].current_index = 2
+ op_data.operators["红"].current_room = "dormitory_1"
+ op_data.operators["夕"].resting_priority = "low"
+ op_data.operators["红"].current_room = "meeting"
+ check_dorm_ordering(tasks, op_data)
+
+ # 如果VIP位已经被占用,则不会生成新任务
+ self.assertEqual(2, len(tasks))
+ # 验证第任务包含换班+宿舍任务
+ self.assertEqual(2, len(tasks[0].plan))
+ # 重复执行不会生成新的
+ check_dorm_ordering(tasks, op_data)
+ self.assertEqual(2, len(tasks))
+ # 验证第任务包宿舍+换班任务
+ self.assertEqual(2, len(tasks[0].plan))
+
+ def test_adjust_three_orders(self):
+ # 测试342跑单任务被拉开
+ task1 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:00", "%Y-%m-%d %H:%M"),
+ task_plan={"task1": "Task 1"},
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="task1",
+ )
+ task2 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:01", "%Y-%m-%d %H:%M"),
+ task_plan={"task2": "Task 2"},
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="task2",
+ )
+ task3 = SchedulerTask(
+ time=datetime.strptime("2023-09-19 10:02", "%Y-%m-%d %H:%M"),
+ task_plan={"task3": "Task 3"},
+ task_type=TaskTypes.RUN_ORDER,
+ meta_data="task3",
+ )
+ tasks = [task1, task2, task3]
+ res = scheduling(
+ tasks, time_now=datetime.strptime("2023-09-19 09:01", "%Y-%m-%d %H:%M")
+ )
+
+ while res is not None and res.meta_data == "task1":
+ task_time = res.time - timedelta(minutes=(2))
+ task = find_next_task(
+ tasks, task_type=TaskTypes.RUN_ORDER, meta_data="task1"
+ )
+ if task is not None:
+ task.time = task_time
+ res = scheduling(
+ tasks,
+ time_now=datetime.strptime("2023-09-19 09:03", "%Y-%m-%d %H:%M"),
+ )
+ else:
+ break
+ # 返还的是应该拉开跑单的任务
+ self.assertNotEqual(res, None)
+
+ def init_opdata(self):
+ agent_base_config = PlanConfig(
+ "稀音,黑键,伊内丝,承曦格雷伊", "稀音,柏喙,伊内丝", "见行者"
+ )
+ plan_config = {
+ "central": [
+ Room("夕", "", ["麒麟R夜刀"]),
+ Room("焰尾", "", ["凯尔希"]),
+ Room("森蚺", "", ["凯尔希"]),
+ Room("令", "", ["火龙S黑角"]),
+ Room("薇薇安娜", "", ["玛恩纳"]),
+ ],
+ "meeting": [
+ Room("伊内丝", "", ["陈", "红"]),
+ Room("见行者", "", ["陈", "红"]),
+ ],
+ "dormitory_1": [
+ Room("塑心", "", []),
+ Room("冰酿", "", []),
+ Room("Free", "", []),
+ Room("Free", "", []),
+ Room("Free", "", []),
+ ],
+ }
+ plan = {
+ "default_plan": Plan(plan_config, agent_base_config),
+ "backup_plans": [],
+ }
+ op_data = Operators(plan)
+ op_data.init_and_validate()
+ # 预设干员位置
+ op_data.operators["冰酿"].current_room = op_data.operators[
+ "塑心"
+ ].current_room = op_data.operators["见行者"].current_room = "dormitory_1"
+ op_data.operators["红"].current_room = op_data.operators[
+ "玛恩纳"
+ ].current_room = "dormitory_1"
+ op_data.operators["冰酿"].current_index = 0
+ op_data.operators["塑心"].current_index = 1
+ op_data.operators["红"].current_index = 2
+ op_data.operators["见行者"].current_index = 3
+ op_data.operators["玛恩纳"].current_index = 4
+ return op_data
diff --git a/arknights_mower/utils/asst.py b/arknights_mower/utils/asst.py
deleted file mode 100644
index e12eb266d..000000000
--- a/arknights_mower/utils/asst.py
+++ /dev/null
@@ -1,265 +0,0 @@
-import ctypes
-import os
-import pathlib
-import platform
-import json
-
-from typing import Union, Dict, List, Any, Type, Optional
-from enum import Enum, IntEnum, unique, auto
-
-JSON = Union[Dict[str, Any], List[Any], int, str, float, bool, Type[None]]
-
-
-class InstanceOptionType(IntEnum):
- touch_type = 2
- deployment_with_pause = 3
-
-
-class Asst:
- CallBackType = ctypes.CFUNCTYPE(
- None, ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p)
- """
- 回调函数,使用实例可参照 my_callback
- :params:
- ``param1 message``: 消息类型
- ``param2 details``: json string
- ``param3 arg``: 自定义参数
- """
-
- @staticmethod
- def load(path: Union[pathlib.Path, str], incremental_path: Optional[Union[pathlib.Path, str]] = None, user_dir: Optional[Union[pathlib.Path, str]] = None) -> bool:
- """
- 加载 dll 及资源
- :params:
- ``path``: DLL及资源所在文件夹路径
- ``incremental_path``: 增量资源所在文件夹路径
- ``user_dir``: 用户数据(日志、调试图片等)写入文件夹路径
- """
-
- platform_values = {
- 'windows': {
- 'libpath': 'MaaCore.dll',
- 'environ_var': 'PATH'
- },
- 'darwin': {
- 'libpath': 'libMaaCore.dylib',
- 'environ_var': 'DYLD_LIBRARY_PATH'
- },
- 'linux': {
- 'libpath': 'libMaaCore.so',
- 'environ_var': 'LD_LIBRARY_PATH'
- }
- }
- lib_import_func = None
-
- platform_type = platform.system().lower()
- if platform_type == 'windows':
- lib_import_func = ctypes.WinDLL
- else:
- lib_import_func = ctypes.CDLL
-
- Asst.__libpath = pathlib.Path(path) / platform_values[platform_type]['libpath']
- try:
- os.environ[platform_values[platform_type]['environ_var']] += os.pathsep + str(path)
- except KeyError:
- os.environ[platform_values[platform_type]['environ_var']] = os.pathsep + str(path)
- Asst.__lib = lib_import_func(str(Asst.__libpath))
- Asst.__set_lib_properties()
-
- ret: bool = True
- if user_dir:
- ret &= Asst.__lib.AsstSetUserDir(str(user_dir).encode('utf-8'))
-
- ret &= Asst.__lib.AsstLoadResource(str(path).encode('utf-8'))
- if incremental_path:
- ret &= Asst.__lib.AsstLoadResource(
- str(incremental_path).encode('utf-8'))
-
- return ret
-
- def __init__(self, callback: CallBackType = None, arg=None):
- """
- :params:
- ``callback``: 回调函数
- ``arg``: 自定义参数
- """
-
- if callback:
- self.__ptr = Asst.__lib.AsstCreateEx(callback, arg)
- else:
- self.__ptr = Asst.__lib.AsstCreate()
-
- def __del__(self):
- Asst.__lib.AsstDestroy(self.__ptr)
- self.__ptr = None
-
- def set_instance_option(self, option_type: InstanceOptionType, option_value: str):
- """
- 设置额外配置
- 参见${MaaAssistantArknights}/src/MaaCore/Assistant.cpp#set_instance_option
- :params:
- ``externa_config``: 额外配置类型
- ``config_value``: 额外配置的值
- :return: 是否设置成功
- """
- return Asst.__lib.AsstSetInstanceOption(self.__ptr,
- int(option_type), option_value.encode('utf-8'))
-
-
- def connect(self, adb_path: str, address: str, config: str = 'General'):
- """
- 连接设备
- :params:
- ``adb_path``: adb 程序的路径
- ``address``: adb 地址+端口
- ``config``: adb 配置,可参考 resource/config.json
- :return: 是否连接成功
- """
- return Asst.__lib.AsstConnect(self.__ptr,
- adb_path.encode('utf-8'), address.encode('utf-8'), config.encode('utf-8'))
-
- TaskId = int
-
- def append_task(self, type_name: str, params: JSON = {}) -> TaskId:
- """
- 添加任务
- :params:
- ``type_name``: 任务类型,请参考 docs/集成文档.md
- ``params``: 任务参数,请参考 docs/集成文档.md
- :return: 任务 ID, 可用于 set_task_params 接口
- """
- return Asst.__lib.AsstAppendTask(self.__ptr, type_name.encode('utf-8'), json.dumps(params, ensure_ascii=False).encode('utf-8'))
-
- def set_task_params(self, task_id: TaskId, params: JSON) -> bool:
- """
- 动态设置任务参数
- :params:
- ``task_id``: 任务 ID, 使用 append_task 接口的返回值
- ``params``: 任务参数,同 append_task 接口,请参考 docs/集成文档.md
- :return: 是否成功
- """
- return Asst.__lib.AsstSetTaskParams(self.__ptr, task_id, json.dumps(params, ensure_ascii=False).encode('utf-8'))
-
- def start(self) -> bool:
- """
- 开始任务
- :return: 是否成功
- """
- return Asst.__lib.AsstStart(self.__ptr)
-
- def stop(self) -> bool:
- """
- 停止并清空所有任务
- :return: 是否成功
- """
- return Asst.__lib.AsstStop(self.__ptr)
-
- def running(self) -> bool:
- """
- 是否正在运行
- :return: 是否正在运行
- """
- return Asst.__lib.AsstRunning(self.__ptr)
-
- @staticmethod
- def log(level: str, message: str) -> None:
- '''
- 打印日志
- :params:
- ``level``: 日志等级标签
- ``message``: 日志内容
- '''
-
- Asst.__lib.AsstLog(level.encode('utf-8'), message.encode('utf-8'))
-
- def get_version(self) -> str:
- """
- 获取DLL版本号
- : return: 版本号
- """
- return Asst.__lib.AsstGetVersion().decode('utf-8')
-
- @staticmethod
- def __set_lib_properties():
- Asst.__lib.AsstSetUserDir.restype = ctypes.c_bool
- Asst.__lib.AsstSetUserDir.argtypes = (
- ctypes.c_char_p,)
-
- Asst.__lib.AsstLoadResource.restype = ctypes.c_bool
- Asst.__lib.AsstLoadResource.argtypes = (
- ctypes.c_char_p,)
-
- Asst.__lib.AsstCreate.restype = ctypes.c_void_p
- Asst.__lib.AsstCreate.argtypes = ()
-
- Asst.__lib.AsstCreateEx.restype = ctypes.c_void_p
- Asst.__lib.AsstCreateEx.argtypes = (
- ctypes.c_void_p, ctypes.c_void_p,)
-
- Asst.__lib.AsstDestroy.argtypes = (ctypes.c_void_p,)
-
- Asst.__lib.AsstSetInstanceOption.restype = ctypes.c_bool
- Asst.__lib.AsstSetInstanceOption.argtypes = (
- ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p,)
-
- Asst.__lib.AsstConnect.restype = ctypes.c_bool
- Asst.__lib.AsstConnect.argtypes = (
- ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,)
-
- Asst.__lib.AsstAppendTask.restype = ctypes.c_int
- Asst.__lib.AsstAppendTask.argtypes = (
- ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p)
-
- Asst.__lib.AsstSetTaskParams.restype = ctypes.c_bool
- Asst.__lib.AsstSetTaskParams.argtypes = (
- ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p)
-
- Asst.__lib.AsstStart.restype = ctypes.c_bool
- Asst.__lib.AsstStart.argtypes = (ctypes.c_void_p,)
-
- Asst.__lib.AsstStop.restype = ctypes.c_bool
- Asst.__lib.AsstStop.argtypes = (ctypes.c_void_p,)
-
- Asst.__lib.AsstRunning.restype = ctypes.c_bool
- Asst.__lib.AsstRunning.argtypes = (ctypes.c_void_p,)
-
- Asst.__lib.AsstGetVersion.restype = ctypes.c_char_p
-
- Asst.__lib.AsstLog.restype = None
- Asst.__lib.AsstLog.argtypes = (
- ctypes.c_char_p, ctypes.c_char_p)
-
-
-@unique
-class Message(Enum):
- """
- 回调消息
- 请参考 docs/回调消息.md
- """
- InternalError = 0
-
- InitFailed = auto()
-
- ConnectionInfo = auto()
-
- AllTasksCompleted = auto()
-
- TaskChainError = 10000
-
- TaskChainStart = auto()
-
- TaskChainCompleted = auto()
-
- TaskChainExtraInfo = auto()
-
- TaskChainStopped = auto()
-
- SubTaskError = 20000
-
- SubTaskStart = auto()
-
- SubTaskCompleted = auto()
-
- SubTaskExtraInfo = auto()
-
- SubTaskStopped = auto()
\ No newline at end of file
diff --git a/arknights_mower/utils/character_recognize.py b/arknights_mower/utils/character_recognize.py
index 7b11d7ab1..11e7bcb6d 100644
--- a/arknights_mower/utils/character_recognize.py
+++ b/arknights_mower/utils/character_recognize.py
@@ -1,238 +1,82 @@
-from __future__ import annotations
-
-import traceback
-from copy import deepcopy
+import lzma
+import pickle
import cv2
import numpy as np
-from matplotlib import pyplot as plt
-from PIL import Image, ImageDraw, ImageFont
-
-from .. import __rootdir__
-from ..data import agent_list
-from . import segment
-from .image import saveimg
-from .log import logger
-from .recognize import RecognizeError
-from ..ocr import ocrhandle
-
-
-def poly_center(poly):
- return (np.average([x[0] for x in poly]), np.average([x[1] for x in poly]))
-
-
-def in_poly(poly, p):
- return poly[0, 0] <= p[0] <= poly[2, 0] and poly[0, 1] <= p[1] <= poly[2, 1]
-
-
-char_map = {}
-agent_sorted = sorted(deepcopy(agent_list), key=len)
-origin = origin_kp = origin_des = None
-
-FLANN_INDEX_KDTREE = 0
-GOOD_DISTANCE_LIMIT = 0.7
-SIFT = cv2.SIFT_create()
-
-def agent_sift_init():
- global origin, origin_kp, origin_des
- if origin is None:
- logger.debug('agent_sift_init')
+from arknights_mower import __rootdir__
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+
+kernel = np.ones((10, 10), np.uint8)
+
+with lzma.open(f"{__rootdir__}/models/operator_select.model", "rb") as f:
+ OP_SELECT = pickle.loads(f.read())
+
+
+def operator_list(img, draw=False):
+ name_y = ((488, 520), (909, 941))
+ line1 = cropimg(img, tuple(zip((600, 1920), name_y[0])))
+ hsv = cv2.cvtColor(line1, cv2.COLOR_RGB2HSV)
+ mask = cv2.inRange(hsv, (98, 140, 200), (102, 255, 255))
+ line1 = cv2.cvtColor(line1, cv2.COLOR_RGB2GRAY)
+ line1[mask > 0] = (255,)
+ line1 = thres2(line1, 140)
+
+ last_line = line1[-1]
+ prev = last_line[0]
+ start = None
+ name_x = []
+ for i in range(1, line1.shape[1]):
+ curr = last_line[i]
+ if prev == 0 and curr == 255 and start and i - start > 186:
+ name_x.append((start + 600, i + 598))
+ elif prev == 255 and curr == 0:
+ start = i
+ prev = curr
+
+ name_p = []
+ for x in name_x:
+ for y in name_y:
+ name_p.append(tuple(zip(x, y)))
+
+ logger.debug(name_p)
+
+ op_name = []
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+
+ for p in name_p:
+ im = cropimg(gray, p)
+ im = thres2(im, 140)
+ im = cv2.copyMakeBorder(im, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,))
+ dilation = cv2.dilate(im, kernel, iterations=1)
+ contours, _ = cv2.findContours(dilation, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
+ rect = map(lambda c: cv2.boundingRect(c), contours)
+ x, y, w, h = sorted(rect, key=lambda c: c[0])[0]
+ im = im[y : y + h, x : x + w]
+ tpl = np.zeros((42, 200), dtype=np.uint8)
+ tpl[: im.shape[0], : im.shape[1]] = im
+ tpl = cv2.copyMakeBorder(tpl, 2, 2, 2, 2, cv2.BORDER_CONSTANT, None, (0,))
+ max_score = 0
+ best_operator = None
+ for operator, template in OP_SELECT.items():
+ result = cv2.matchTemplate(tpl, template, cv2.TM_CCORR_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if max_val > max_score:
+ max_score = max_val
+ best_operator = operator
+ op_name.append(best_operator)
+
+ logger.debug(op_name)
- height = width = 2000
- lnum = 25
- cell = height // lnum
-
- img = np.zeros((height, width, 3), dtype=np.uint8)
- img = Image.fromarray(img)
-
- font = ImageFont.truetype(
- f'{__rootdir__}/fonts/SourceHanSansSC-Bold.otf', size=30, encoding='utf-8'
- )
- chars = sorted(list(set(''.join([x for x in agent_list]))))
- assert len(chars) <= (lnum - 2) * (lnum - 2)
-
- for idx, char in enumerate(chars):
- x, y = idx % (lnum - 2) + 1, idx // (lnum - 2) + 1
- char_map[(x, y)] = char
- ImageDraw.Draw(img).text(
- (x * cell, y * cell), char, (255, 255, 255), font=font
- )
-
- origin = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2GRAY)
- origin_kp, origin_des = SIFT.detectAndCompute(origin, None)
-
-
-def sift_recog(query, resolution, draw=False,reverse = False):
- """
- 使用 SIFT 提取特征点识别干员名称
- """
- agent_sift_init()
- query = cv2.cvtColor(np.array(query), cv2.COLOR_RGB2GRAY)
- if reverse :
- # 干员总览界面图像色度反转
- query = 255 -query
- # the height & width of query image
- height, width = query.shape
-
- multi = 2 * (resolution / 1080)
- query = cv2.resize(query, (int(width * multi), int(height * multi)))
- query_kp, query_des = SIFT.detectAndCompute(query, None)
-
- # build FlannBasedMatcher
- index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
- search_params = dict(checks=50)
- flann = cv2.FlannBasedMatcher(index_params, search_params)
- matches = flann.knnMatch(query_des, origin_des, k=2)
+ if draw:
+ display = img.copy()
+ for p in name_p:
+ cv2.rectangle(display, p[0], p[1], (255, 0, 0), 3)
- # store all the good matches as per Lowe's ratio test
- good = []
- for x, y in matches:
- if x.distance < GOOD_DISTANCE_LIMIT * y.distance:
- good.append(x)
+ from matplotlib import pyplot as plt
- if draw:
- result = cv2.drawMatches(query, query_kp, origin, origin_kp, good, None)
- plt.imshow(result, 'gray')
+ plt.imshow(display)
plt.show()
- count = {}
-
- for x in good:
- x, y = origin_kp[x.trainIdx].pt
- c = char_map[(int(x) // 80, int(y) // 80)]
- count[c] = count.get(c, 0) + 1
-
- best = None
- best_score = 0
- for x in agent_sorted:
- score = 0
- for c in set(x):
- score += count.get(c, -1)
- if score > best_score:
- best = x
- best_score = score
-
- logger.debug(f'segment.sift_recog: {count}, {best}')
-
- return best
-
-
-def agent(img, draw=False):
- """
- 识别干员总览界面的干员名称
- """
- try:
- height, width, _ = img.shape
- resolution = height
- left, right = 0, width
-
- # 异形屏适配
- while np.max(img[:, right - 1]) < 100:
- right -= 1
- while np.max(img[:, left]) < 100:
- left += 1
-
- # 去除左侧干员详情
- x0 = left + 1
- while not (
- img[height - 1, x0 - 1, 0] > img[height - 1, x0, 0] + 10
- and abs(int(img[height - 1, x0, 0]) - int(img[height - 1, x0 + 1, 0])) < 5
- ):
- x0 += 1
-
- # 获取分割结果
- ret, ocr = segment.agent(img, draw)
-
- # 确定位置后开始精确识别
- ret_succ = []
- ret_fail = []
- ret_agent = []
- for poly in ret:
- found_ocr, fx = None, 0
- for x in ocr:
- cx, cy = poly_center(x[2])
- if in_poly(poly, (cx + x0, cy)) and cx > fx:
- fx = cx
- found_ocr = x
- __img = img[poly[0, 1]: poly[2, 1], poly[0, 0]: poly[2, 0]]
- try:
- if found_ocr is not None:
- x = found_ocr
- if x[1] in agent_list and x[1] not in ['砾', '陈']: # ocr 经常会把这两个搞错
- ret_agent.append(x[1])
- ret_succ.append(poly)
- continue
- res = sift_recog(__img, resolution, draw)
- if (res is not None) and res in agent_list:
- ret_agent.append(res)
- ret_succ.append(poly)
- continue
- logger.warning(
- f'干员名称识别异常:{x[1]} 为不存在的数据,请报告至 https://github.com/Konano/arknights-mower/issues'
- )
- saveimg(__img, 'failure_agent')
- raise Exception("启动 Plan B")
- else:
- if 80 <= np.min(__img):
- continue
- res = sift_recog(__img, resolution, draw)
- if res is not None:
- ret_agent.append(res)
- ret_succ.append(poly)
- continue
- logger.warning(f'干员名称识别异常:区域 {poly.tolist()}')
- saveimg(__img, 'failure_agent')
- raise Exception("启动 Plan B")
- ret_fail.append(poly)
- raise Exception("启动 Plan B")
- except Exception as e:
- # 大哥不行了,二哥上!
- name = segment.read_screen(__img,
- langurage="chi_sim",
- type="text")
- logger.warning(f'备选方案识别结果: {name}')
- ret_fail.append(poly)
- if name in ocr_error.keys():
- name = ocr_error[name]
- else:
- ret_agent.append(name)
- ret_succ.append(poly)
- continue
- if len(ret_fail):
- saveimg(img, 'failure')
- if draw:
- __img = img.copy()
- cv2.polylines(__img, ret_fail, True, (255, 0, 0), 3, cv2.LINE_AA)
- plt.imshow(__img)
- plt.show()
-
- logger.debug(f'character_recognize.agent: {ret_agent}')
- logger.debug(f'character_recognize.agent: {[x.tolist() for x in ret]}')
- return list(zip(ret_agent, ret_succ))
-
- except Exception as e:
- logger.debug(traceback.format_exc())
- saveimg(img, 'failure_agent')
- raise RecognizeError(e)
-
-def agent_name(__img, height,reverse = False, draw: bool = False):
- query = cv2.cvtColor(np.array(__img), cv2.COLOR_RGB2GRAY)
- h, w= query.shape
- dim = (w*4, h*4)
- # resize image
- resized = cv2.resize(__img, dim, interpolation=cv2.INTER_AREA)
- ocr = ocrhandle.predict(resized)
- name = ''
- try:
- if len(ocr) > 0 and ocr[0][1] in agent_list and ocr[0][1] not in ['砾', '陈']:
- name = ocr[0][1]
- else:
- res = sift_recog(__img, height, draw, reverse)
- if (res is not None) and res in agent_list:
- name = res
- else:
- raise Exception("识别错误")
- except Exception as e:
- saveimg(__img, 'failure_agent')
- return name
+ return tuple(zip(op_name, name_p))
diff --git a/arknights_mower/utils/config.py b/arknights_mower/utils/config.py
deleted file mode 100644
index 2c189557e..000000000
--- a/arknights_mower/utils/config.py
+++ /dev/null
@@ -1,225 +0,0 @@
-from __future__ import annotations
-
-import shutil
-import sys
-try:
- from collections.abc import Mapping
-except ImportError:
- from collections import Mapping
-from pathlib import Path
-from typing import Any
-
-from ruamel.yaml.comments import CommentedSeq
-
-from .. import __pyinstall__, __rootdir__, __system__
-from . import typealias as tp
-from .yaml import yaml
-
-# The lowest version supported
-VERSION_SUPPORTED_MIN = 1
-VERSION_SUPPORTED_MAX = 1
-
-
-__ydoc = None
-
-BASE_CONSTRUCT_PLAN: dict[str, tp.BasePlan]
-OPE_PLAN: list[tp.OpePlan]
-
-
-def __dig_mapping(path: str):
- path = path.split('/')
- parent_maps = path[:-1]
- current_map = __ydoc
- for idx, k in enumerate(parent_maps):
- if k not in current_map:
- raise KeyError(path)
- current_map = current_map[k]
- if not isinstance(current_map, Mapping):
- raise TypeError(
- 'config key %s is not a mapping' % '/'.join(path[: idx + 1])
- )
- return current_map, path[-1]
-
-
-def __get(path: str, default: Any = None):
- try:
- current_map, k = __dig_mapping(path)
- except (KeyError, TypeError) as e:
- return default
- if current_map is None or k not in current_map or current_map[k] is None:
- return default
- return current_map[k]
-
-
-def __get_list(path: str, default: Any = list()):
- item = __get(path)
- if item is None:
- return default
- elif not isinstance(item, CommentedSeq):
- return [item]
- else:
- return list(item)
-
-
-def __set(path: str, value: Any):
- try:
- current_map, k = __dig_mapping(path)
- except (KeyError, TypeError):
- return
- current_map[k] = value
-
-
-def build_config(path: str, module: bool) -> None:
- """ build config via template """
- global __ydoc
- with Path(f'{__rootdir__}/templates/config.yaml').open('r', encoding='utf8') as f:
- loader = yaml.load_all(f)
- next(loader) # discard first document (used for comment)
- __ydoc = next(loader)
- init_debug(module)
- __set('debug/logfile/path', str(LOGFILE_PATH.resolve()))
- __set('debug/screenshot/path', str(SCREENSHOT_PATH.resolve()))
- with Path(path).open('w', encoding='utf8') as f:
- yaml.dump(__ydoc, f)
-
-
-def load_config(path: str) -> None:
- """ load config from PATH """
- global __ydoc, PATH
- PATH = Path(path)
- with PATH.open('r', encoding='utf8') as f:
- __ydoc = yaml.load(f)
- if not VERSION_SUPPORTED_MIN <= __get('version', 1) <= VERSION_SUPPORTED_MAX:
- raise RuntimeError('The current version of the config file is not supported')
- init_config()
-
-
-def save_config() -> None:
- """ save config into PATH """
- global PATH
- with PATH.open('w', encoding='utf8') as f:
- yaml.dump(__ydoc, f)
-
-
-def init_config() -> None:
- """ init config from __ydoc """
- global ADB_BINARY, ADB_DEVICE, ADB_CONNECT
- ADB_BINARY = __get('device/adb_binary', [])
- ADB_DEVICE = __get('device/adb_device', [])
- ADB_CONNECT = __get('device/adb_connect', [])
- if shutil.which('adb') is not None:
- ADB_BINARY.append(shutil.which('adb'))
-
- global ADB_BUILDIN
- ADB_BUILDIN = None
-
- global ADB_SERVER_IP, ADB_SERVER_PORT, ADB_SERVER_TIMEOUT
- ADB_SERVER_IP = __get('device/adb_server/ip', '127.0.0.1')
- ADB_SERVER_PORT = __get('device/adb_server/port', 5037)
- ADB_SERVER_TIMEOUT = __get('device/adb_server/timeout', 5)
-
- global ADB_CONTROL_CLIENT
- ADB_CONTROL_CLIENT = __get('device/adb_control_client', 'scrcpy')
-
- global MNT_TOUCH_DEVICE
- MNT_TOUCH_DEVICE = __get('device/mnt_touch_device', None)
-
- global MNT_PORT
- MNT_PORT = __get('device/mnt_port', 20937)
-
- global MNT_COMPATIBILITY_MODE
- MNT_COMPATIBILITY_MODE = __get('device/mnt_compatibility_mode', False)
-
- global USERNAME, PASSWORD
- USERNAME = __get('account/username', None)
- PASSWORD = __get('account/password', None)
-
- global APPNAME
- APPNAME = __get('app/package_name', 'com.hypergryph.arknights') + \
- '/' + __get('app/activity_name', 'com.u8.sdk.U8UnityContext')
-
- global DEBUG_MODE, LOGFILE_PATH, LOGFILE_AMOUNT, SCREENSHOT_PATH, SCREENSHOT_MAXNUM
- DEBUG_MODE = __get('debug/enabled', False)
- LOGFILE_PATH = __get('debug/logfile/path', None)
- LOGFILE_AMOUNT = __get('debug/logfile/amount', 3)
- SCREENSHOT_PATH = __get('debug/screenshot/path', None)
- SCREENSHOT_MAXNUM = __get('debug/screenshot/max_total', 20)
-
- global MAX_RETRYTIME
- MAX_RETRYTIME = __get('behavior/max_retry', 5)
-
- global OCR_APIKEY
- OCR_APIKEY = __get('ocr/ocr_space_api', 'c7431c9d7288957')
-
- global BASE_CONSTRUCT_PLAN
- BASE_CONSTRUCT_PLAN = __get('arrangement', None)
-
- global SCHEDULE_PLAN
- SCHEDULE_PLAN = __get('schedule', None)
-
- global RECRUIT_PRIORITY, SHOP_PRIORITY
- RECRUIT_PRIORITY = __get('priority/recruit', None)
- SHOP_PRIORITY = __get('priority/shop', None)
-
- global OPE_TIMES, OPE_POTION, OPE_ORIGINITE, OPE_ELIMINATE, OPE_PLAN
- OPE_TIMES = __get('operation/times', -1)
- OPE_POTION = __get('operation/potion', 0)
- OPE_ORIGINITE = __get('operation/originite', 0)
- OPE_ELIMINATE = int(__get('operation/eliminate', 0)) # convert bool to int
- OPE_PLAN = __get('operation/plan', None)
- if OPE_PLAN is not None:
- OPE_PLAN = [x.split(',') for x in OPE_PLAN]
- OPE_PLAN = [[x[0], int(x[1])] for x in OPE_PLAN]
-
-
-def update_ope_plan(plan: list[tp.OpePlan]) -> None:
- """ update operation plan """
- global OPE_PLAN
- OPE_PLAN = plan
- print([f'{x[0]},{x[1]}' for x in OPE_PLAN])
- __set('operation/plan', [f'{x[0]},{x[1]}' for x in OPE_PLAN])
- # TODO 其他参数也应该更新
- save_config()
-
-
-def init_debug(module: bool) -> None:
- """ init LOGFILE_PATH & SCREENSHOT_PATH """
- global LOGFILE_PATH, SCREENSHOT_PATH
- if __pyinstall__:
- LOGFILE_PATH = Path(sys.executable).parent.joinpath('log')
- SCREENSHOT_PATH = Path(sys.executable).parent.joinpath('screenshot')
- elif module:
- if __system__ == 'windows':
- LOGFILE_PATH = Path.home().joinpath('arknights-mower')
- SCREENSHOT_PATH = Path.home().joinpath('arknights-mower/screenshot')
- elif __system__ == 'linux':
- LOGFILE_PATH = Path('/var/log/arknights-mower')
- SCREENSHOT_PATH = Path('/var/log/arknights-mower/screenshot')
- elif __system__ == 'darwin':
- LOGFILE_PATH = Path('/var/log/arknights-mower')
- SCREENSHOT_PATH = Path('/var/log/arknights-mower/screenshot')
- else:
- raise NotImplementedError(f'Unknown system: {__system__}')
- else:
- LOGFILE_PATH = __rootdir__.parent.joinpath('log')
- SCREENSHOT_PATH = __rootdir__.parent.joinpath('screenshot')
-
-
-def init_adb_buildin() -> Path:
- """ init ADB_BUILDIN & ADB_BUILDIN_DIR """
- global ADB_BUILDIN_DIR, ADB_BUILDIN
- ADB_BUILDIN = None
- if __pyinstall__:
- ADB_BUILDIN_DIR = Path(sys.executable).parent.joinpath('adb-buildin')
- elif __system__ == 'windows':
- ADB_BUILDIN_DIR = Path.home().joinpath('arknights-mower/adb-buildin')
- elif __system__ == 'linux':
- ADB_BUILDIN_DIR = Path.home().joinpath('.arknights-mower')
- elif __system__ == 'darwin':
- ADB_BUILDIN_DIR = Path.home().joinpath('.arknights-mower')
- else:
- raise NotImplementedError(f'Unknown system: {__system__}')
- return ADB_BUILDIN_DIR
-
-
-init_config()
diff --git a/arknights_mower/utils/config/__init__.py b/arknights_mower/utils/config/__init__.py
new file mode 100644
index 000000000..cd34c6dc3
--- /dev/null
+++ b/arknights_mower/utils/config/__init__.py
@@ -0,0 +1,94 @@
+import json
+from datetime import datetime, timedelta
+from queue import Queue
+from threading import Event
+from typing import Any, Optional
+
+import requests
+import yaml
+from pydantic import BaseModel
+from yamlcore import CoreDumper, CoreLoader
+
+from arknights_mower.utils.config.conf import Conf
+from arknights_mower.utils.config.plan import PlanModel
+from arknights_mower.utils.path import get_path
+
+conf_path = get_path("@app/conf.yml")
+plan_path = get_path("@app/plan.json")
+
+
+def save_conf():
+ with conf_path.open("w", encoding="utf8") as f:
+ yaml.dump(
+ conf.model_dump(),
+ f,
+ Dumper=CoreDumper,
+ encoding="utf-8",
+ default_flow_style=False,
+ allow_unicode=True,
+ )
+
+
+def load_conf():
+ global conf
+ if not conf_path.is_file():
+ conf_path.parent.mkdir(exist_ok=True)
+ conf = Conf()
+ save_conf()
+ return
+ with conf_path.open("r", encoding="utf-8") as f:
+ conf = Conf(**yaml.load(f, Loader=CoreLoader))
+
+
+conf: Conf
+load_conf()
+
+
+def save_plan():
+ with plan_path.open("w", encoding="utf-8") as f:
+ json.dump(plan.model_dump(exclude_none=True), f, ensure_ascii=False, indent=2)
+
+
+def load_plan():
+ global plan
+ if not plan_path.is_file():
+ plan_path.parent.mkdir(exist_ok=True)
+ plan = PlanModel()
+ save_plan()
+ return
+ with plan_path.open("r", encoding="utf-8") as f:
+ plan = PlanModel(**json.load(f))
+
+
+plan: PlanModel
+load_plan()
+
+
+stop_mower = Event()
+stop_maa = Event()
+
+# 日志
+log_queue = Queue()
+wh = None
+
+
+class DroidCast(BaseModel):
+ session: Any = requests.Session()
+ port: int = 0
+ process: Any = None
+
+
+droidcast = DroidCast()
+
+screenshot_time: datetime = datetime.now() - timedelta(
+ milliseconds=conf.screenshot_interval
+)
+screenshot_avg: Optional[int] = None
+screenshot_count: int = 0
+
+
+# 常量
+APP_ACTIVITY_NAME = "com.u8.sdk.U8UnityContext"
+MAX_RETRYTIME = 5
+MNT_COMPATIBILITY_MODE = False
+MNT_PORT = 20937
diff --git a/arknights_mower/utils/config/conf.py b/arknights_mower/utils/config/conf.py
new file mode 100644
index 000000000..6fedc2b59
--- /dev/null
+++ b/arknights_mower/utils/config/conf.py
@@ -0,0 +1,580 @@
+from pydantic import BaseModel, model_validator
+from pydantic_core import PydanticUndefined
+
+
+class ConfModel(BaseModel):
+ @model_validator(mode="before")
+ @classmethod
+ def nested_defaults(cls, data):
+ for name, field in cls.model_fields.items():
+ if name not in data:
+ if field.default is PydanticUndefined:
+ data[name] = field.annotation()
+ else:
+ data[name] = field.default
+ return data
+
+
+class CluePart(ConfModel):
+ class CreditFightConf(ConfModel):
+ direction: str = "Right"
+ "部署方向"
+ operator: str = "风笛"
+ "使用干员"
+ squad: int = 1
+ "编队序号"
+ x: int = 5
+ "横坐标"
+ y: int = 3
+ "纵坐标"
+
+ maa_credit_fight: bool = True
+ "信用作战开关"
+ credit_fight: CreditFightConf
+ "信用作战设置"
+ enable_party: int = 1
+ "线索收集"
+ leifeng_mode: int = 1
+ "雷锋模式"
+ maa_mall_blacklist: str = "加急许可,碳,碳素,家具零件"
+ "黑名单"
+ maa_mall_buy: str = "招聘许可,技巧概要·卷2"
+ "优先购买"
+ maa_mall_ignore_blacklist_when_full: bool = False
+ "信用溢出时无视黑名单"
+
+
+class EmailPart(ConfModel):
+ class CustomSMTPServerConf(ConfModel):
+ enable: bool = False
+ "启用自定义邮箱"
+ server: str = ""
+ "SMTP服务器"
+ encryption: str = "starttls"
+ "加密方式"
+ ssl_port: int = 587
+ "端口号"
+
+ mail_enable: int = 0
+ "邮件提醒"
+ account: str = ""
+ "邮箱用户名"
+ pass_code: str = ""
+ "邮箱密码"
+ recipient: list[str] = []
+ "收件人"
+ custom_smtp_server: CustomSMTPServerConf
+ "自定义邮箱"
+ mail_subject: str = "[Mower通知]"
+ "标题前缀"
+ notification_level: str = "INFO"
+ "邮件通知等级"
+
+
+class ExtraPart(ConfModel):
+ class WebViewConf(ConfModel):
+ port: int = 58000
+ "端口号"
+ width: int = 1450
+ "窗口宽度"
+ height: int = 850
+ "窗口高度"
+ token: str = ""
+ "远程连接密钥"
+ scale: float = 1
+ "网页缩放"
+ tray: bool = True
+ "托盘图标"
+
+ class WaitingSceneConf(ConfModel):
+ CONNECTING: tuple[int, int] = (1, 10)
+ UNKNOWN: tuple[int, int] = (1, 10)
+ LOADING: tuple[int, int] = (2, 30)
+ LOGIN_LOADING: tuple[int, int] = (3, 10)
+ LOGIN_MAIN_NOENTRY: tuple[int, int] = (3, 10)
+ OPERATOR_ONGOING: tuple[int, int] = (10, 30)
+
+ start_automatically: bool = False
+ "启动后自动开始任务"
+ webview: WebViewConf
+ "GUI相关设置"
+ theme: str = "light"
+ "界面主题"
+ screenshot_interval: int = 500
+ "截图最短间隔(毫秒)"
+ screenshot: float = 24
+ "截图保留时长(小时)"
+ check_for_updates: bool = True
+ "检查更新"
+ waiting_scene: WaitingSceneConf
+ "等待时间"
+
+
+class LongTaskPart(ConfModel):
+ class RogueConf(ConfModel):
+ squad: str = ""
+ "分队"
+ roles: str = ""
+ "职业"
+ core_char: str = ""
+ "干员"
+ use_support: bool = False
+ "开局干员使用助战"
+ use_nonfriend_support: bool = False
+ "开局干员使用非好友助战"
+ mode: int = 1
+ "策略"
+ refresh_trader_with_dice: bool = False
+ "刷新商店(指路鳞)"
+ expected_collapsal_paradigms: list[str] = [
+ "目空一些",
+ "睁眼瞎",
+ "图像损坏",
+ "一抹黑",
+ ]
+ "需要刷的坍缩范式"
+
+ class SSSConf(ConfModel):
+ type: int = 1
+ "关卡"
+ ec: int = 1
+ "导能单元"
+
+ class ReclamationAlgorithmConf(ConfModel):
+ timeout: int = 30
+ "生息演算和隐秘战线的超时时间"
+
+ class SecretFrontConf(ConfModel):
+ target: str = "结局A"
+ "隐秘战线结局"
+
+ class SignInConf(ConfModel):
+ enable: bool = True
+ "签到活动开关"
+
+ maa_rg_enable: int = 0
+ "大型任务"
+ maa_long_task_type: str = "rogue"
+ "大型任务类型"
+ maa_rg_sleep_max: str = "0:00"
+ "开始时间"
+ maa_rg_sleep_min: str = "0:00"
+ "停止时间"
+ maa_rg_theme: str = "Mizuki"
+ "肉鸽主题"
+ rogue: RogueConf
+ "肉鸽设置"
+ sss: SSSConf
+ "保全设置"
+ reclamation_algorithm: ReclamationAlgorithmConf
+ "生息演算"
+ secret_front: SecretFrontConf
+ "隐秘战线结局"
+ sign_in: SignInConf
+ "签到活动"
+
+
+class MaaPart(ConfModel):
+ maa_path: str = "D:\\MAA-v4.13.0-win-x64"
+ maa_conn_preset: str = "General"
+ maa_touch_option: str = "maatouch"
+
+
+class RecruitPart(ConfModel):
+ recruit_enable: bool = True
+ "公招开关"
+ recruit_robot: bool = True
+ "保留支援机械标签"
+ recruitment_permit: int = 30
+ "三星招募阈值"
+ recruit_gap: float = 9
+ "启动间隔"
+ recruit_auto_5: int = 1
+ "五星招募策略,1自动,2手动"
+ recruit_auto_only5: bool = False
+ "五星词条组合唯一时自动选择"
+
+
+class RegularTaskPart(ConfModel):
+ class MaaDailyPlan(BaseModel):
+ medicine: int = 0
+ stage: list[str]
+ weekday: str
+
+ class MaaDailyPlan1(BaseModel):
+ stage: str | list[str]
+ 周一: int
+ 周二: int
+ 周三: int
+ 周四: int
+ 周五: int
+ 周六: int
+ 周日: int
+
+ check_mail_enable: bool = True
+ "领取邮件奖励"
+ maa_enable: bool = True
+ "日常任务"
+ maa_gap: float = 3
+ "日常任务间隔"
+ maa_expiring_medicine: bool = True
+ "自动使用将要过期(约3天)的理智药"
+ exipring_medicine_on_weekend: bool = False
+ "仅在周末使用将要过期的理智药"
+ maa_eat_stone: bool = False
+ "无限吃源石"
+ maa_weekly_plan: list[MaaDailyPlan] = [
+ {"medicine": 0, "stage": [""], "weekday": "周一"},
+ {"medicine": 0, "stage": [""], "weekday": "周二"},
+ {"medicine": 0, "stage": [""], "weekday": "周三"},
+ {"medicine": 0, "stage": [""], "weekday": "周四"},
+ {"medicine": 0, "stage": [""], "weekday": "周五"},
+ {"medicine": 0, "stage": [""], "weekday": "周六"},
+ {"medicine": 0, "stage": [""], "weekday": "周日"},
+ ]
+ "周计划"
+ maa_weekly_plan1: list[MaaDailyPlan1] = [
+ {
+ "stage": ["点x删除"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["把鼠标放到问号上查看帮助"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["自定义关卡3"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["Annihilation"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["1-7"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["LS-6"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["CE-6"],
+ "周一": 0,
+ "周二": 1,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["AP-5"],
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["SK-6"],
+ "周一": 1,
+ "周二": 0,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0,
+ },
+ {
+ "stage": ["CA-5"],
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-A-2"],
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-A-2"],
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-B-2"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 0,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0,
+ },
+ {
+ "stage": ["PR-B-1"],
+ "周一": 1,
+ "周二": 1,
+ "周三": 0,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0,
+ },
+ {
+ "stage": ["PR-C-2"],
+ "周一": 0,
+ "周二": 0,
+ "周三": 1,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-C-1"],
+ "周一": 0,
+ "周二": 0,
+ "周三": 1,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-D-2"],
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ {
+ "stage": ["PR-D-1"],
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ },
+ ]
+ "周计划(新)"
+ maa_depot_enable: bool = False
+ "仓库物品混合读取"
+ visit_friend: bool = True
+ "访问好友"
+ report_enable: bool = True
+ "读取基报"
+
+
+class RIICPart(ConfModel):
+ class RunOrderGrandetModeConf(ConfModel):
+ enable: bool = True
+ "葛朗台跑单开关"
+ buffer_time: int = 15
+ "缓冲时间"
+ back_to_index: bool = False
+ "跑单前返回基建首页"
+
+ drone_count_limit: int = 100
+ "无人机使用阈值"
+ drone_room: str = ""
+ "无人机使用房间"
+ drone_interval: float = 3
+ "无人机加速间隔"
+ free_blacklist: str = ""
+ "宿舍黑名单"
+ reload_room: str = ""
+ "搓玉补货房间"
+ run_order_delay: float = 3
+ "跑单前置延时"
+ resting_threshold: float = 0.65
+ "心情阈值"
+ run_order_grandet_mode: RunOrderGrandetModeConf
+ "葛朗台跑单"
+ free_room: bool = False
+ "宿舍不养闲人模式"
+
+
+class SimulatorPart(ConfModel):
+ class SimulatorConf(ConfModel):
+ name: str = ""
+ "名称"
+ index: str | int = "-1"
+ "多开编号"
+ simulator_folder: str = ""
+ "文件夹"
+ wait_time: int = 30
+ "启动时间"
+ hotkey: str = ""
+ "老板键"
+
+ class CustomScreenshotConf(ConfModel):
+ command: str = "adb -s 127.0.0.1:5555 shell screencap -p 2>/dev/null"
+ "截图命令"
+ enable: bool = False
+ "是否启用自定义截图"
+
+ class TapToLaunchGameConf(ConfModel):
+ enable: bool = False
+ "点击屏幕启动游戏"
+ x: int = 0
+ "横坐标"
+ y: int = 0
+ "纵坐标"
+
+ class DroidCastConf(ConfModel):
+ enable: bool = True
+ "使用DroidCast截图"
+ rotate: bool = False
+ "将截图旋转180度"
+
+ adb: str = "127.0.0.1:16384"
+ "ADB连接地址"
+ simulator: SimulatorConf
+ "模拟器"
+ maa_adb_path: str = "D:\\Program Files\\Nox\\bin\\adb.exe"
+ "ADB路径"
+ close_simulator_when_idle: bool = False
+ "任务结束后关闭游戏"
+ package_type: int = 1
+ "游戏服务器"
+ custom_screenshot: CustomScreenshotConf
+ "自定义截图"
+ tap_to_launch_game: TapToLaunchGameConf
+ "点击屏幕启动游戏"
+ exit_game_when_idle: bool = True
+ "任务结束后退出游戏"
+ close_simulator_when_idle: bool = False
+ "任务结束后关闭模拟器"
+ fix_mumu12_adb_disconnect: bool = False
+ "关闭MuMu模拟器12时结束adb进程"
+ touch_method: str = "scrcpy"
+ "触控模式"
+ droidcast: DroidCastConf
+ "DroidCast截图设置"
+
+
+class SKLandPart(ConfModel):
+ class SKLandAccount(BaseModel):
+ account: str = ""
+ "账号"
+ password: str = ""
+ "密码"
+ cultivate_select: bool = True
+ "服务器"
+ isCheck: bool = True
+ "签到"
+ sign_in_bilibili: bool = False
+ "官服签到"
+ sign_in_official: bool = False
+ "B服签到"
+
+ skland_enable: bool = False
+ "森空岛签到"
+ skland_info: list[SKLandAccount] = []
+ "森空岛账号"
+
+
+class Conf(
+ CluePart,
+ EmailPart,
+ ExtraPart,
+ LongTaskPart,
+ MaaPart,
+ RecruitPart,
+ RegularTaskPart,
+ RIICPart,
+ SimulatorPart,
+ SKLandPart,
+):
+ @property
+ def APPNAME(self):
+ return (
+ "com.hypergryph.arknights"
+ if self.package_type == 1
+ else "com.hypergryph.arknights.bilibili"
+ )
+
+ @property
+ def RG(self):
+ return self.maa_rg_enable == 1 and self.maa_long_task_type == "rogue"
+
+ @property
+ def SSS(self):
+ return self.maa_rg_enable == 1 and self.maa_long_task_type == "sss"
+
+ @property
+ def RA(self):
+ return self.maa_rg_enable == 1 and self.maa_long_task_type == "ra"
+
+ @property
+ def SF(self):
+ return self.maa_rg_enable == 1 and self.maa_long_task_type == "sf"
+
+ @property
+ def run_order_buffer_time(self):
+ """
+ > 0 葛朗台跑单的缓冲时间
+ <= 0 无人机跑单
+ """
+ if self.run_order_grandet_mode.enable:
+ return self.run_order_grandet_mode.buffer_time
+ return -1
diff --git a/arknights_mower/utils/config/plan.py b/arknights_mower/utils/config/plan.py
new file mode 100644
index 000000000..61e96e26b
--- /dev/null
+++ b/arknights_mower/utils/config/plan.py
@@ -0,0 +1,112 @@
+from __future__ import annotations
+
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class PlanConf(BaseModel):
+ ling_xi: int = 1
+ "令夕模式,1感知 2烟火 3均衡"
+ max_resting_count: int = 4
+ "最大组人数"
+ exhaust_require: str = ""
+ "耗尽"
+ rest_in_full: str = ""
+ "回满"
+ resting_priority: str = ""
+ "低优先级"
+ workaholic: str = ""
+ "0心情工作(主力宿舍黑名单)"
+ refresh_trading: str = ""
+ "跑单时间刷新干员"
+
+
+class BackupPlanConf(PlanConf):
+ free_blacklist: str = ""
+ "(非主力)宿舍黑名单"
+
+
+class Plans(BaseModel):
+ agent: str
+ group: str = ""
+ replacement: list[str] = []
+
+
+class Facility(BaseModel):
+ name: str = ""
+ plans: list[Plans] = []
+ product: Optional[str] = None
+
+
+class Plan1(BaseModel):
+ central: Optional[Facility] = None
+ "控制中枢"
+ meeting: Optional[Facility] = None
+ "会客室"
+ factory: Optional[Facility] = None
+ "加工站"
+ contact: Optional[Facility] = None
+ "办公室"
+ train: Optional[Facility] = None
+ "训练室"
+ dormitory_1: Optional[Facility] = None
+ dormitory_2: Optional[Facility] = None
+ dormitory_3: Optional[Facility] = None
+ dormitory_4: Optional[Facility] = None
+ room_1_1: Optional[Facility] = None
+ room_1_2: Optional[Facility] = None
+ room_1_3: Optional[Facility] = None
+ room_2_1: Optional[Facility] = None
+ room_2_2: Optional[Facility] = None
+ room_2_3: Optional[Facility] = None
+ room_3_1: Optional[Facility] = None
+ room_3_2: Optional[Facility] = None
+ room_3_3: Optional[Facility] = None
+
+
+class Task(BaseModel):
+ central: Optional[list[str]] = None
+ "控制中枢"
+ meeting: Optional[list[str]] = None
+ "会客室"
+ factory: Optional[list[str]] = None
+ "加工站"
+ contact: Optional[list[str]] = None
+ "办公室"
+ train: Optional[list[str]] = None
+ "训练室"
+ dormitory_1: Optional[list[str]] = None
+ dormitory_2: Optional[list[str]] = None
+ dormitory_3: Optional[list[str]] = None
+ dormitory_4: Optional[list[str]] = None
+ room_1_1: Optional[list[str]] = None
+ room_1_2: Optional[list[str]] = None
+ room_1_3: Optional[list[str]] = None
+ room_2_1: Optional[list[str]] = None
+ room_2_2: Optional[list[str]] = None
+ room_2_3: Optional[list[str]] = None
+ room_3_1: Optional[list[str]] = None
+ room_3_2: Optional[list[str]] = None
+ room_3_3: Optional[list[str]] = None
+
+
+class Trigger(BaseModel):
+ left: str | Trigger = ""
+ operator: str = ""
+ right: str | Trigger = ""
+
+
+class BackupPlan(BaseModel):
+ conf: BackupPlanConf = {}
+ plan: Plan1 = {}
+ task: Task = {}
+ trigger: Trigger = {}
+ trigger_timing: str = "AFTER_PLANNING"
+
+
+class PlanModel(BaseModel):
+ default: str = "plan1"
+ plan1: Plan1 = Plan1()
+ conf: PlanConf = PlanConf()
+ backup_plans: list[BackupPlan] = []
diff --git a/arknights_mower/utils/csleep.py b/arknights_mower/utils/csleep.py
new file mode 100644
index 000000000..e83d344d7
--- /dev/null
+++ b/arknights_mower/utils/csleep.py
@@ -0,0 +1,23 @@
+import time
+from datetime import datetime, timedelta
+
+from arknights_mower.utils import config
+
+
+class MowerExit(Exception):
+ pass
+
+
+def csleep(interval: float = 1):
+ """check and sleep"""
+ stop_time = datetime.now() + timedelta(seconds=interval)
+ while True:
+ if config.stop_mower.is_set():
+ raise MowerExit
+ remaining = stop_time - datetime.now()
+ if remaining > timedelta(seconds=1):
+ time.sleep(1)
+ elif remaining > timedelta():
+ time.sleep(remaining.total_seconds())
+ else:
+ return
diff --git a/arknights_mower/utils/datetime.py b/arknights_mower/utils/datetime.py
index 735844cc7..5cbb711b6 100644
--- a/arknights_mower/utils/datetime.py
+++ b/arknights_mower/utils/datetime.py
@@ -1,14 +1,35 @@
from datetime import datetime
+
import pytz
+
def the_same_day(a: datetime = None, b: datetime = None) -> bool:
if a is None or b is None:
return False
return a.year == b.year and a.month == b.month and a.day == b.day
-def the_same_time(a: datetime = None, b: datetime = None) -> bool:
+
+def the_same_time(a: datetime | None = None, b: datetime | None = None) -> bool:
if a is None or b is None:
return False
- return a.year == b.year and a.month == b.month and a.day == b.day and a.hour ==b.hour and a.minute==b.minute and a.second == b.second
+ return abs(a - b).total_seconds() < 1.5
+
+
def get_server_weekday():
- return datetime.now(pytz.timezone('Asia/Dubai')).weekday()
\ No newline at end of file
+ return datetime.now(pytz.timezone("Asia/Dubai")).weekday()
+
+
+# newbing说用这个来定义休息时间省事
+def format_time(seconds):
+ if seconds < 0: # 权宜之计 配合刷生息演算
+ return f"{0} 分钟" # 权宜之计 配合刷生息演算
+ # 计算小时和分钟
+ rest_hours = int(seconds / 3600)
+ rest_minutes = int((seconds % 3600) / 60)
+ # 根据小时是否为零来决定是否显示
+ if rest_hours == 0:
+ return f"{rest_minutes} 分钟"
+ elif rest_minutes == 0:
+ return f"{rest_hours} 小时"
+ else:
+ return f"{rest_hours} 小时 {rest_minutes} 分钟"
diff --git a/arknights_mower/utils/depot.py b/arknights_mower/utils/depot.py
new file mode 100644
index 000000000..69b074130
--- /dev/null
+++ b/arknights_mower/utils/depot.py
@@ -0,0 +1,231 @@
+import json
+import os
+from datetime import datetime
+
+import pandas as pd
+
+# from .log import logger
+from arknights_mower.data import key_mapping
+
+# from typing import Dict, List, Union
+from arknights_mower.utils.path import get_path
+
+
+def 读取仓库():
+ path = get_path("@app/tmp/cultivate.json")
+ if not os.path.exists(path):
+ 创建json()
+ with open(path, "r", encoding="utf-8") as f:
+ depotinfo = json.load(f)
+ 物品数量 = depotinfo["data"]["items"]
+ 新物品1 = {
+ key_mapping[item["id"]][2]: int(item["count"])
+ for item in 物品数量
+ if int(item["count"]) != 0
+ }
+
+ csv_path = get_path("@app/tmp/depotresult.csv")
+ if not os.path.exists(csv_path):
+ 创建csv()
+
+ # 读取CSV文件
+ depotinfo = pd.read_csv(csv_path)
+
+ # 取出最后一行数据中的物品信息并进行合并
+ 最后一行物品 = json.loads(depotinfo.iloc[-1, 1])
+ 新物品 = {**最后一行物品, **新物品1} # 合并字典
+ 新物品json = {}
+ for item in 新物品:
+ 新物品json[key_mapping[item][0]] = 新物品[item]
+ time = depotinfo.iloc[-1, 0]
+
+ sort = {
+ "A常用": [
+ "至纯源石",
+ "合成玉",
+ "寻访凭证",
+ "十连寻访凭证",
+ "龙门币",
+ "高级凭证",
+ "资质凭证",
+ "招聘许可",
+ ],
+ "B经验卡": ["基础作战记录", "初级作战记录", "中级作战记录", "高级作战记录"],
+ "C稀有度5": ["烧结核凝晶", "晶体电子单元", "D32钢", "双极纳米片", "聚合剂"],
+ "D稀有度4": [
+ "提纯源岩",
+ "改量装置",
+ "聚酸酯块",
+ "糖聚块",
+ "异铁块",
+ "酮阵列",
+ "转质盐聚块",
+ "切削原液",
+ "精炼溶剂",
+ "晶体电路",
+ "炽合金块",
+ "聚合凝胶",
+ "白马醇",
+ "三水锰矿",
+ "五水研磨石",
+ "RMA70-24",
+ "环烃预制体",
+ "固化纤维板",
+ ],
+ "E稀有度3": [
+ "固源岩组",
+ "全新装置",
+ "聚酸酯组",
+ "糖组",
+ "异铁组",
+ "酮凝集组",
+ "转质盐组",
+ "化合切削液",
+ "半自然溶剂",
+ "晶体元件",
+ "炽合金",
+ "凝胶",
+ "扭转醇",
+ "轻锰矿",
+ "研磨石",
+ "RMA70-12",
+ "环烃聚质",
+ "褐素纤维",
+ ],
+ "F稀有度2": ["固源岩", "装置", "聚酸酯", "糖", "异铁", "酮凝集"],
+ "G稀有度1": ["源岩", "破损装置", "酯原料", "代糖", "异铁碎片", "双酮"],
+ "H模组": ["模组数据块", "数据增补仪", "数据增补条"],
+ "I技能书": ["技巧概要·卷3", "技巧概要·卷2", "技巧概要·卷1"],
+ "J芯片相关": [
+ "重装双芯片",
+ "重装芯片组",
+ "重装芯片",
+ "狙击双芯片",
+ "狙击芯片组",
+ "狙击芯片",
+ "医疗双芯片",
+ "医疗芯片组",
+ "医疗芯片",
+ "术师双芯片",
+ "术师芯片组",
+ "术师芯片",
+ "先锋双芯片",
+ "先锋芯片组",
+ "先锋芯片",
+ "近卫双芯片",
+ "近卫芯片组",
+ "近卫芯片",
+ "辅助双芯片",
+ "辅助芯片组",
+ "辅助芯片",
+ "特种双芯片",
+ "特种芯片组",
+ "特种芯片",
+ "采购凭证",
+ "芯片助剂",
+ ],
+ "K未分类": [],
+ }
+ classified_data = {}
+ classified_data["K未分类"] = {}
+ for category, items in sort.items():
+ classified_data[category] = {
+ item: {"number": 0, "sort": key_mapping[item][4], "icon": item}
+ for item in items
+ }
+
+ for key, value in 新物品.items():
+ found_category = False
+ for category, items in sort.items():
+ if key in items:
+ classified_data[category][key] = {
+ "number": value,
+ "sort": key_mapping[key][4],
+ "icon": key,
+ }
+ found_category = True
+ break
+ if not found_category:
+ # 如果未找到匹配的分类,则放入 "K未分类" 中
+ classified_data["K未分类"][key] = {
+ "number": value,
+ "sort": key_mapping[key][4],
+ "icon": key,
+ }
+
+ classified_data["B经验卡"]["全部经验(计算)"] = {
+ "number": (
+ classified_data["B经验卡"]["基础作战记录"]["number"] * 200
+ + classified_data["B经验卡"]["初级作战记录"]["number"] * 400
+ + classified_data["B经验卡"]["中级作战记录"]["number"] * 1000
+ + classified_data["B经验卡"]["高级作战记录"]["number"] * 2000
+ ),
+ "sort": 9999999,
+ "icon": "EXP",
+ }
+ 合成玉数量 = classified_data["A常用"].get("合成玉", {"number": 0})["number"]
+ 寻访凭证数量 = (
+ classified_data["A常用"].get("寻访凭证", {"number": 0})["number"]
+ + classified_data["A常用"].get("十连寻访凭证", {"number": 0})["number"] * 10
+ )
+ 源石数量 = classified_data["A常用"].get("至纯源石", {"number": 0})["number"]
+ 源石碎片 = classified_data["K未分类"].get("源石碎片", {"number": 0})["number"]
+
+ 土 = classified_data["F稀有度2"].get("固源岩", {"number": 0})["number"]
+ classified_data["A常用"]["玉+卷"] = {
+ "number": round(合成玉数量 / 600 + 寻访凭证数量, 1),
+ "sort": 9999999,
+ "icon": "寻访凭证",
+ }
+ classified_data["A常用"]["玉+卷+石"] = {
+ "number": round((合成玉数量 + 源石数量 * 180) / 600 + 寻访凭证数量, 1),
+ "sort": 9999999,
+ "icon": "寻访凭证",
+ }
+ classified_data["A常用"]["额外+碎片"] = {
+ "number": round(
+ (合成玉数量 + 源石数量 * 180 + int(源石碎片 / 2) * 20) / 600 + 寻访凭证数量,
+ 1,
+ ),
+ "sort": 9999999,
+ "icon": "寻访凭证",
+ }
+ 待定碎片 = int(土 / 2)
+ classified_data["A常用"]["额外+碎片+土"] = {
+ "number": round(
+ (合成玉数量 + 源石数量 * 180 + int((源石碎片 + 待定碎片) / 2) * 20) / 600
+ + 寻访凭证数量,
+ 1,
+ ),
+ "sort": 9999999,
+ "icon": "寻访凭证",
+ }
+ return [
+ classified_data,
+ json.dumps(新物品json),
+ str(datetime.fromtimestamp(int(time))),
+ ]
+
+
+def 创建csv():
+ path = get_path("@app/tmp/depotresult.csv")
+ now_time = int(datetime.now().timestamp()) - 24 * 3600
+ result = [
+ now_time,
+ json.dumps({"还未开始过扫描": 0}, ensure_ascii=False),
+ json.dumps({"空": ""}, ensure_ascii=False),
+ ]
+ depotinfo = pd.DataFrame([result], columns=["Timestamp", "Data", "json"])
+ depotinfo.to_csv(path, mode="a", index=False, header=True, encoding="utf-8")
+
+
+def 创建json():
+ path = get_path("@app/tmp/cultivate.json")
+ a = {
+ "code": 0,
+ "message": "OK",
+ "timestamp": "1719065002",
+ "data": {"items": [{"id": "31063", "count": "0"}]},
+ }
+ with open(path, "w", encoding="utf-8") as f:
+ json.dump(a, f)
diff --git a/arknights_mower/utils/deprecated.py b/arknights_mower/utils/deprecated.py
new file mode 100644
index 000000000..9601077a3
--- /dev/null
+++ b/arknights_mower/utils/deprecated.py
@@ -0,0 +1,13 @@
+import functools
+
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.traceback import caller_info
+
+
+def deprecated(func):
+ @functools.wraps(func)
+ def new_func(*args, **kwargs):
+ logger.warning(f"已弃用的函数{func.__name__}被{caller_info()}调用")
+ return func(*args, **kwargs)
+
+ return new_func
diff --git a/arknights_mower/utils/detector.py b/arknights_mower/utils/detector.py
index 8e5c4ff68..206b895ae 100644
--- a/arknights_mower/utils/detector.py
+++ b/arknights_mower/utils/detector.py
@@ -1,186 +1,41 @@
-import cv2
-import numpy as np
-
-from .. import __rootdir__
-from . import typealias as tp
-from .image import loadimg
-from .log import logger
-from .matcher import Matcher
-
-
-def confirm(img: tp.Image) -> tp.Coordinate:
- """
- 检测是否出现确认界面
- """
- height, width, _ = img.shape
-
- # 4 scan lines: left, right, up, down
- left, right = width // 4 * 3 - 10, width // 4 * 3 + 10
- up, down = height // 2 - 10, height // 2 + 10
-
- # the R/G/B must be the same for a single pixel in the specified area
- if (img[up:down, left:right, :-1] != img[up:down, left:right, 1:]).any():
- return None
-
- # the pixel average of the specified area must be in the vicinity of 55
- if abs(np.mean(img[up:down, left:right]) - 55) > 5:
- return None
-
- # set a new scan line: up
- up = 0
- for i in range(down, height):
- for j in range(left, right):
- if np.ptp(img[i, j]) != 0 or abs(img[i, j, 0] - 13) > 3:
- break
- elif j == right-1:
- up = i
- if up:
- break
- if up == 0:
- return None
-
- # set a new scan line: down
- down = 0
- for i in range(up, height):
- for j in range(left, right):
- if np.ptp(img[i, j]) != 0 or abs(img[i, j, 0] - 13) > 3:
- down = i
- break
- if down:
- break
- if down == 0:
- return None
-
- # detect successful
- point = (width // 2, (up + down) // 2)
- logger.debug(f'detector.confirm: {point}')
- return point
-
-
-def infra_notification(img: tp.Image) -> tp.Coordinate:
- """
- 检测基建内是否存在蓝色通知
- 前置条件:已经处于基建内
- """
- height, width, _ = img.shape
-
- # set a new scan line: right
- right = width
- while np.max(img[:, right-1]) < 100:
- right -= 1
- right -= 1
-
- # set a new scan line: up
- up = 0
- for i in range(height):
- if img[i, right, 0] < 100 < img[i, right, 1] < img[i, right, 2]:
- up = i
- break
- if up == 0:
- return None
-
- # set a new scan line: down
- down = 0
- for i in range(up, height):
- if not (img[i, right, 0] < 100 < img[i, right, 1] < img[i, right, 2]):
- down = i
- break
- if down == 0:
- return None
-
- # detect successful
- point = (right - 10, (up + down) // 2)
- logger.debug(f'detector.infra_notification: {point}')
- return point
-
-
-def announcement_close(img: tp.Image) -> tp.Coordinate:
- """
- 检测「关闭公告」按钮
- """
- height, width, _ = img.shape
-
- # 4 scan lines: left, right, up, down
- up, down = 0, height // 4
- left, right = width // 4 * 3, width
-
- sumx, sumy, cnt = 0, 0, 0
- for i in range(up, down):
- line_cnt = 0
- for j in range(left, right):
- if np.ptp(img[i, j]) == 0 and abs(img[i, j, 0] - 89) < 3: # condition
- sumx += i
- sumy += j
- cnt += 1
- line_cnt += 1
-
- # the number of pixels meeting the condition in one line reaches 100
- if line_cnt >= 100:
- return None
-
- # the number of pixels meeting the condition reaches 2000
- if cnt >= 2000:
- # detect successful
- point = (sumy // cnt, sumx // cnt)
- logger.debug(f'detector.announcement_close: {point}')
- return point
-
- return None
-
-
-def visit_next(img: tp.Image) -> tp.Coordinate:
- """
- 检测「访问下位」按钮
- """
- height, width, _ = img.shape
-
- # set a new scan line: right
- right = width
- while np.max(img[:, right-1]) < 100:
- right -= 1
- right -= 1
-
- # set a new scan line: up
- up = 0
- for i in range(height):
- if img[i, right, 0] > 150 > img[i, right, 1] > 40 > img[i, right, 2]:
- up = i
- break
- if up == 0:
- return None
-
- # set a new scan line: down
- down = 0
- for i in range(up, height):
- if not (img[i, right, 0] > 150 > img[i, right, 1] > 40 > img[i, right, 2]):
- down = i
- break
- if down == 0:
- return None
-
- # detect successful
- point = (right - 10, (up + down) // 2)
- logger.debug(f'detector.visit_next: {point}')
- return point
-
-
-on_shift = loadimg(f'{__rootdir__}/resources/agent_on_shift.png', True)
-distracted = loadimg(f'{__rootdir__}/resources/distracted.png', True)
-resting = loadimg(f'{__rootdir__}/resources/agent_resting.png', True)
-
-def is_on_shift(img: tp.Image) -> bool:
- """
- 检测干员是否正在工作中
- """
- matcher = Matcher(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))
- if matcher.match(on_shift, judge=False) is not None:
- return True
- if matcher.match(resting, judge=False) is not None:
- return True
- if matcher.match(distracted, judge=False) is not None:
- return False
- width = img.shape[1]
- __width = int(width * 0.7)
- left_up = np.count_nonzero(np.all(img[0, :__width] <= 62, axis=1) & np.all(30 <= img[0, :__width], axis=1)) / __width
- logger.debug(f'is_on_shift: {left_up}')
- return left_up > 0.3
+import numpy as np
+
+from . import typealias as tp
+from .log import logger
+
+
+def infra_notification(img: tp.Image) -> tp.Coordinate:
+ """
+ 检测基建内是否存在蓝色通知
+ 前置条件:已经处于基建内
+ """
+ height, width, _ = img.shape
+
+ # set a new scan line: right
+ right = width
+ while np.max(img[:, right - 1]) < 100:
+ right -= 1
+ right -= 1
+
+ # set a new scan line: up
+ up = 0
+ for i in range(height):
+ if img[i, right, 0] < 100 < img[i, right, 1] < img[i, right, 2]:
+ up = i
+ break
+ if up == 0:
+ return None
+
+ # set a new scan line: down
+ down = 0
+ for i in range(up, height):
+ if not (img[i, right, 0] < 100 < img[i, right, 1] < img[i, right, 2]):
+ down = i
+ break
+ if down == 0:
+ return None
+
+ # detect successful
+ point = (right - 10, (up + down) // 2)
+ logger.debug(f"detector.infra_notification: {point}")
+ return point
diff --git a/arknights_mower/utils/device/__init__.py b/arknights_mower/utils/device/__init__.py
index a2e37beb8..e69de29bb 100644
--- a/arknights_mower/utils/device/__init__.py
+++ b/arknights_mower/utils/device/__init__.py
@@ -1,2 +0,0 @@
-from .adb_client.const import KeyCode
-from .device import Device
diff --git a/arknights_mower/utils/device/adb_client/__init__.py b/arknights_mower/utils/device/adb_client/__init__.py
index 26d4b019b..e69de29bb 100644
--- a/arknights_mower/utils/device/adb_client/__init__.py
+++ b/arknights_mower/utils/device/adb_client/__init__.py
@@ -1 +0,0 @@
-from .core import Client as ADBClient
diff --git a/arknights_mower/utils/device/adb_client/const.py b/arknights_mower/utils/device/adb_client/const.py
index 8ca68fa60..9350840c5 100644
--- a/arknights_mower/utils/device/adb_client/const.py
+++ b/arknights_mower/utils/device/adb_client/const.py
@@ -1,101 +1,101 @@
class KeyCode:
- """ https://developer.android.com/reference/android/view/KeyEvent.html """
+ """https://developer.android.com/reference/android/view/KeyEvent.html"""
- KEYCODE_CALL = 5 # 拨号键
- KEYCODE_ENDCALL = 6 # 挂机键
- KEYCODE_HOME = 3 # Home 键
- KEYCODE_MENU = 82 # 菜单键
- KEYCODE_BACK = 4 # 返回键
- KEYCODE_SEARCH = 84 # 搜索键
- KEYCODE_CAMERA = 27 # 拍照键
- KEYCODE_FOCUS = 80 # 对焦键
- KEYCODE_POWER = 26 # 电源键
- KEYCODE_NOTIFICATION = 83 # 通知键
- KEYCODE_MUTE = 91 # 话筒静音键
- KEYCODE_VOLUME_MUTE = 164 # 扬声器静音键
- KEYCODE_VOLUME_UP = 24 # 音量 + 键
- KEYCODE_VOLUME_DOWN = 25 # 音量 - 键
- KEYCODE_ENTER = 66 # 回车键
- KEYCODE_ESCAPE = 111 # ESC 键
- KEYCODE_DPAD_CENTER = 23 # 导航键 >> 确定键
- KEYCODE_DPAD_UP = 19 # 导航键 >> 向上
- KEYCODE_DPAD_DOWN = 20 # 导航键 >> 向下
- KEYCODE_DPAD_LEFT = 21 # 导航键 >> 向左
- KEYCODE_DPAD_RIGHT = 22 # 导航键 >> 向右
- KEYCODE_MOVE_HOME = 122 # 光标移动到开始键
- KEYCODE_MOVE_END = 123 # 光标移动到末尾键
- KEYCODE_PAGE_UP = 92 # 向上翻页键
- KEYCODE_PAGE_DOWN = 93 # 向下翻页键
- KEYCODE_DEL = 67 # 退格键
- KEYCODE_FORWARD_DEL = 112 # 删除键
- KEYCODE_INSERT = 124 # 插入键
- KEYCODE_TAB = 61 # Tab 键
- KEYCODE_NUM_LOCK = 143 # 小键盘锁
- KEYCODE_CAPS_LOCK = 115 # 大写锁定键
- KEYCODE_BREAK = 121 # Break / Pause 键
- KEYCODE_SCROLL_LOCK = 116 # 滚动锁定键
- KEYCODE_ZOOM_IN = 168 # 放大键
- KEYCODE_ZOOM_OUT = 169 # 缩小键
- KEYCODE_0 = 7 # 0
- KEYCODE_1 = 8 # 1
- KEYCODE_2 = 9 # 2
- KEYCODE_3 = 10 # 3
- KEYCODE_4 = 11 # 4
- KEYCODE_5 = 12 # 5
- KEYCODE_6 = 13 # 6
- KEYCODE_7 = 14 # 7
- KEYCODE_8 = 15 # 8
- KEYCODE_9 = 16 # 9
- KEYCODE_A = 29 # A
- KEYCODE_B = 30 # B
- KEYCODE_C = 31 # C
- KEYCODE_D = 32 # D
- KEYCODE_E = 33 # E
- KEYCODE_F = 34 # F
- KEYCODE_G = 35 # G
- KEYCODE_H = 36 # H
- KEYCODE_I = 37 # I
- KEYCODE_J = 38 # J
- KEYCODE_K = 39 # K
- KEYCODE_L = 40 # L
- KEYCODE_M = 41 # M
- KEYCODE_N = 42 # N
- KEYCODE_O = 43 # O
- KEYCODE_P = 44 # P
- KEYCODE_Q = 45 # Q
- KEYCODE_R = 46 # R
- KEYCODE_S = 47 # S
- KEYCODE_T = 48 # T
- KEYCODE_U = 49 # U
- KEYCODE_V = 50 # V
- KEYCODE_W = 51 # W
- KEYCODE_X = 52 # X
- KEYCODE_Y = 53 # Y
- KEYCODE_Z = 54 # Z
- KEYCODE_PLUS = 81 # +
- KEYCODE_MINUS = 69 # -
- KEYCODE_STAR = 17 # *
- KEYCODE_SLASH = 76 # /
- KEYCODE_EQUALS = 70 # =
- KEYCODE_AT = 77 # @
- KEYCODE_POUND = 18 # #
- KEYCODE_APOSTROPHE = 75 # '
- KEYCODE_BACKSLASH = 73 # \
- KEYCODE_COMMA = 55 # ,
- KEYCODE_PERIOD = 56 # .
- KEYCODE_LEFT_BRACKET = 71 # [
- KEYCODE_RIGHT_BRACKET = 72 # ]
- KEYCODE_SEMICOLON = 74 # ;
- KEYCODE_GRAVE = 68 # `
- KEYCODE_SPACE = 62 # 空格键
- KEYCODE_MEDIA_PLAY = 126 # 多媒体键 >> 播放
- KEYCODE_MEDIA_STOP = 86 # 多媒体键 >> 停止
- KEYCODE_MEDIA_PAUSE = 127 # 多媒体键 >> 暂停
- KEYCODE_MEDIA_PLAY_PAUSE = 85 # 多媒体键 >> 播放 / 暂停
+ KEYCODE_CALL = 5 # 拨号键
+ KEYCODE_ENDCALL = 6 # 挂机键
+ KEYCODE_HOME = 3 # Home 键
+ KEYCODE_MENU = 82 # 菜单键
+ KEYCODE_BACK = 4 # 返回键
+ KEYCODE_SEARCH = 84 # 搜索键
+ KEYCODE_CAMERA = 27 # 拍照键
+ KEYCODE_FOCUS = 80 # 对焦键
+ KEYCODE_POWER = 26 # 电源键
+ KEYCODE_NOTIFICATION = 83 # 通知键
+ KEYCODE_MUTE = 91 # 话筒静音键
+ KEYCODE_VOLUME_MUTE = 164 # 扬声器静音键
+ KEYCODE_VOLUME_UP = 24 # 音量 + 键
+ KEYCODE_VOLUME_DOWN = 25 # 音量 - 键
+ KEYCODE_ENTER = 66 # 回车键
+ KEYCODE_ESCAPE = 111 # ESC 键
+ KEYCODE_DPAD_CENTER = 23 # 导航键 >> 确定键
+ KEYCODE_DPAD_UP = 19 # 导航键 >> 向上
+ KEYCODE_DPAD_DOWN = 20 # 导航键 >> 向下
+ KEYCODE_DPAD_LEFT = 21 # 导航键 >> 向左
+ KEYCODE_DPAD_RIGHT = 22 # 导航键 >> 向右
+ KEYCODE_MOVE_HOME = 122 # 光标移动到开始键
+ KEYCODE_MOVE_END = 123 # 光标移动到末尾键
+ KEYCODE_PAGE_UP = 92 # 向上翻页键
+ KEYCODE_PAGE_DOWN = 93 # 向下翻页键
+ KEYCODE_DEL = 67 # 退格键
+ KEYCODE_FORWARD_DEL = 112 # 删除键
+ KEYCODE_INSERT = 124 # 插入键
+ KEYCODE_TAB = 61 # Tab 键
+ KEYCODE_NUM_LOCK = 143 # 小键盘锁
+ KEYCODE_CAPS_LOCK = 115 # 大写锁定键
+ KEYCODE_BREAK = 121 # Break / Pause 键
+ KEYCODE_SCROLL_LOCK = 116 # 滚动锁定键
+ KEYCODE_ZOOM_IN = 168 # 放大键
+ KEYCODE_ZOOM_OUT = 169 # 缩小键
+ KEYCODE_0 = 7 # 0
+ KEYCODE_1 = 8 # 1
+ KEYCODE_2 = 9 # 2
+ KEYCODE_3 = 10 # 3
+ KEYCODE_4 = 11 # 4
+ KEYCODE_5 = 12 # 5
+ KEYCODE_6 = 13 # 6
+ KEYCODE_7 = 14 # 7
+ KEYCODE_8 = 15 # 8
+ KEYCODE_9 = 16 # 9
+ KEYCODE_A = 29 # A
+ KEYCODE_B = 30 # B
+ KEYCODE_C = 31 # C
+ KEYCODE_D = 32 # D
+ KEYCODE_E = 33 # E
+ KEYCODE_F = 34 # F
+ KEYCODE_G = 35 # G
+ KEYCODE_H = 36 # H
+ KEYCODE_I = 37 # I
+ KEYCODE_J = 38 # J
+ KEYCODE_K = 39 # K
+ KEYCODE_L = 40 # L
+ KEYCODE_M = 41 # M
+ KEYCODE_N = 42 # N
+ KEYCODE_O = 43 # O
+ KEYCODE_P = 44 # P
+ KEYCODE_Q = 45 # Q
+ KEYCODE_R = 46 # R
+ KEYCODE_S = 47 # S
+ KEYCODE_T = 48 # T
+ KEYCODE_U = 49 # U
+ KEYCODE_V = 50 # V
+ KEYCODE_W = 51 # W
+ KEYCODE_X = 52 # X
+ KEYCODE_Y = 53 # Y
+ KEYCODE_Z = 54 # Z
+ KEYCODE_PLUS = 81 # +
+ KEYCODE_MINUS = 69 # -
+ KEYCODE_STAR = 17 # *
+ KEYCODE_SLASH = 76 # /
+ KEYCODE_EQUALS = 70 # =
+ KEYCODE_AT = 77 # @
+ KEYCODE_POUND = 18 # #
+ KEYCODE_APOSTROPHE = 75 # '
+ KEYCODE_BACKSLASH = 73 # \
+ KEYCODE_COMMA = 55 # ,
+ KEYCODE_PERIOD = 56 # .
+ KEYCODE_LEFT_BRACKET = 71 # [
+ KEYCODE_RIGHT_BRACKET = 72 # ]
+ KEYCODE_SEMICOLON = 74 # ;
+ KEYCODE_GRAVE = 68 # `
+ KEYCODE_SPACE = 62 # 空格键
+ KEYCODE_MEDIA_PLAY = 126 # 多媒体键 >> 播放
+ KEYCODE_MEDIA_STOP = 86 # 多媒体键 >> 停止
+ KEYCODE_MEDIA_PAUSE = 127 # 多媒体键 >> 暂停
+ KEYCODE_MEDIA_PLAY_PAUSE = 85 # 多媒体键 >> 播放 / 暂停
KEYCODE_MEDIA_FAST_FORWARD = 90 # 多媒体键 >> 快进
- KEYCODE_MEDIA_REWIND = 89 # 多媒体键 >> 快退
- KEYCODE_MEDIA_NEXT = 87 # 多媒体键 >> 下一首
- KEYCODE_MEDIA_PREVIOUS = 88 # 多媒体键 >> 上一首
- KEYCODE_MEDIA_CLOSE = 128 # 多媒体键 >> 关闭
- KEYCODE_MEDIA_EJECT = 129 # 多媒体键 >> 弹出
- KEYCODE_MEDIA_RECORD = 130 # 多媒体键 >> 录音
+ KEYCODE_MEDIA_REWIND = 89 # 多媒体键 >> 快退
+ KEYCODE_MEDIA_NEXT = 87 # 多媒体键 >> 下一首
+ KEYCODE_MEDIA_PREVIOUS = 88 # 多媒体键 >> 上一首
+ KEYCODE_MEDIA_CLOSE = 128 # 多媒体键 >> 关闭
+ KEYCODE_MEDIA_EJECT = 129 # 多媒体键 >> 弹出
+ KEYCODE_MEDIA_RECORD = 130 # 多媒体键 >> 录音
diff --git a/arknights_mower/utils/device/adb_client/core.py b/arknights_mower/utils/device/adb_client/core.py
index 2eed6d64a..1d96740d7 100644
--- a/arknights_mower/utils/device/adb_client/core.py
+++ b/arknights_mower/utils/device/adb_client/core.py
@@ -1,21 +1,23 @@
-from __future__ import annotations
-
import socket
import subprocess
import time
from typing import Optional, Union
-from ... import config
-from ...log import logger
-from .session import Session
-from .socket import Socket
-from .utils import adb_buildin, run_cmd
+from arknights_mower import __system__
+from arknights_mower.utils import config
+from arknights_mower.utils.csleep import csleep
+from arknights_mower.utils.device.adb_client.session import Session
+from arknights_mower.utils.device.adb_client.socket import Socket
+from arknights_mower.utils.device.adb_client.utils import run_cmd
+from arknights_mower.utils.log import logger
-class Client(object):
- """ ADB Client """
+class Client:
+ """ADB Client"""
- def __init__(self, device_id: str = None, connect: str = None, adb_bin: str = None) -> None:
+ def __init__(
+ self, device_id: str = None, connect: str = None, adb_bin: str = None
+ ) -> None:
self.device_id = device_id
self.connect = connect
self.adb_bin = adb_bin
@@ -26,57 +28,67 @@ def __init__(self, device_id: str = None, connect: str = None, adb_bin: str = No
def __init_adb(self) -> None:
if self.adb_bin is not None:
return
- for adb_bin in config.ADB_BINARY:
- logger.debug(f'try adb binary: {adb_bin}')
- if self.__check_adb(adb_bin):
- self.adb_bin = adb_bin
- return
- if config.ADB_BUILDIN is None:
- adb_buildin()
- if self.__check_adb(config.ADB_BUILDIN):
- self.adb_bin = config.ADB_BUILDIN
+ adb_bin = config.conf.maa_adb_path
+ logger.debug(f"try adb binary: {adb_bin}")
+ if self.__check_adb(adb_bin):
+ self.adb_bin = adb_bin
return
raise RuntimeError("Can't start adb server")
def __init_device(self) -> None:
# wait for the newly started ADB server to probe emulators
- time.sleep(1)
- if self.device_id is None or self.device_id not in config.ADB_DEVICE:
+ csleep(1)
+ if self.device_id is None or self.device_id != config.conf.adb:
self.device_id = self.__choose_devices()
- if self.device_id is None or self.device_id not in config.ADB_DEVICE:
- if self.connect is None or self.device_id not in config.ADB_CONNECT:
- for connect in config.ADB_CONNECT:
- Session().connect(connect)
+ if self.device_id is None:
+ if self.connect is None:
+ Session().connect(config.conf.adb)
else:
Session().connect(self.connect)
self.device_id = self.__choose_devices()
+ elif self.connect is None:
+ Session().connect(self.device_id)
+
+ # if self.device_id is None or self.device_id not in config.ADB_DEVICE:
+ # if self.connect is None or self.device_id not in config.ADB_CONNECT:
+ # for connect in config.ADB_CONNECT:
+ # Session().connect(connect)
+ # else:
+ # Session().connect(self.connect)
+ # self.device_id = self.__choose_devices()
+ logger.info(self.__available_devices())
if self.device_id not in self.__available_devices():
- logger.error('未检测到相应设备。请运行 `adb devices` 确认列表中列出了目标模拟器或设备。')
- raise RuntimeError('Device connection failure')
+ logger.error(
+ "未检测到相应设备。请运行 `adb devices` 确认列表中列出了目标模拟器或设备。"
+ )
+ raise RuntimeError("Device connection failure")
def __choose_devices(self) -> Optional[str]:
- """ choose available devices """
+ """choose available devices"""
devices = self.__available_devices()
- for device in config.ADB_DEVICE:
- if device in devices:
- return device
- # if len(devices) > 0:
- # logger.debug(devices[0])
- # return devices[0]
+ if config.conf.adb in devices:
+ return config.conf.adb
+ if len(devices) > 0 and config.conf.adb == "":
+ logger.debug(devices[0])
+ return devices[0]
def __available_devices(self) -> list[str]:
- """ return available devices """
- return [x[0] for x in Session().devices_list() if x[1] != 'offline']
+ """return available devices"""
+ return [x[0] for x in Session().devices_list() if x[1] != "offline"]
def __exec(self, cmd: str, adb_bin: str = None) -> None:
- """ exec command with adb_bin """
- logger.debug(f'client.__exec: {cmd}')
+ """exec command with adb_bin"""
+ logger.debug(f"client.__exec: {cmd}")
if adb_bin is None:
adb_bin = self.adb_bin
- subprocess.run([adb_bin, cmd], check=True)
+ subprocess.run(
+ [adb_bin, cmd],
+ check=True,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ )
def __run(self, cmd: str, restart: bool = True) -> Optional[bytes]:
- """ run command with Session """
+ """run command with Session"""
error_limit = 3
while True:
try:
@@ -84,24 +96,24 @@ def __run(self, cmd: str, restart: bool = True) -> Optional[bytes]:
except (socket.timeout, ConnectionRefusedError, RuntimeError):
if restart and error_limit > 0:
error_limit -= 1
- self.__exec('kill-server')
- self.__exec('start-server')
+ self.__exec("kill-server")
+ self.__exec("start-server")
time.sleep(10)
continue
return
def check_server_alive(self, restart: bool = True) -> bool:
- """ check adb server if it works """
- return self.__run('host:version', restart) is not None
+ """check adb server if it works"""
+ return self.__run("host:version", restart) is not None
def __check_adb(self, adb_bin: str) -> bool:
- """ check adb_bin if it works """
+ """check adb_bin if it works"""
try:
- self.__exec('start-server', adb_bin)
+ self.__exec("start-server", adb_bin)
if self.check_server_alive(False):
return True
- self.__exec('kill-server', adb_bin)
- self.__exec('start-server', adb_bin)
+ self.__exec("kill-server", adb_bin)
+ self.__exec("start-server", adb_bin)
time.sleep(10)
if self.check_server_alive(False):
return True
@@ -111,14 +123,14 @@ def __check_adb(self, adb_bin: str) -> bool:
return False
def session(self) -> Session:
- """ get a session between adb client and adb server """
+ """get a session between adb client and adb server"""
if not self.check_server_alive():
- raise RuntimeError('ADB server is not working')
+ raise RuntimeError("ADB server is not working")
return Session().device(self.device_id)
def run(self, cmd: str) -> Optional[bytes]:
- """ run adb exec command """
- logger.debug(f'command: {cmd}')
+ """run adb exec command"""
+ logger.debug(f"command: {cmd}")
error_limit = 3
while True:
try:
@@ -127,48 +139,57 @@ def run(self, cmd: str) -> Optional[bytes]:
except (socket.timeout, ConnectionRefusedError, RuntimeError) as e:
if error_limit > 0:
error_limit -= 1
- self.__exec('kill-server')
- self.__exec('start-server')
+ self.__exec("kill-server")
+ self.__exec("start-server")
time.sleep(10)
self.__init_device()
continue
raise e
if len(resp) <= 256:
- logger.debug(f'response: {repr(resp)}')
+ logger.debug(f"response: {repr(resp)}")
return resp
- def cmd(self, cmd: str, decode: bool = False) -> Union[bytes, str]:
- """ run adb command with adb_bin """
- cmd = [self.adb_bin, '-s', self.device_id] + cmd.split(' ')
+ def cmd(self, cmd: str | list[str], decode: bool = False) -> Union[bytes, str]:
+ """run adb command with adb_bin"""
+ if isinstance(cmd, str):
+ cmd = cmd.split(" ")
+ cmd = [self.adb_bin, "-s", self.device_id] + cmd
return run_cmd(cmd, decode)
def cmd_shell(self, cmd: str, decode: bool = False) -> Union[bytes, str]:
- """ run adb shell command with adb_bin """
- cmd = [self.adb_bin, '-s', self.device_id, 'shell'] + cmd.split(' ')
+ """run adb shell command with adb_bin"""
+ cmd = [self.adb_bin, "-s", self.device_id, "shell"] + cmd.split(" ")
return run_cmd(cmd, decode)
def cmd_push(self, filepath: str, target: str) -> None:
- """ push file into device with adb_bin """
- cmd = [self.adb_bin, '-s', self.device_id, 'push', filepath, target]
+ """push file into device with adb_bin"""
+ cmd = [self.adb_bin, "-s", self.device_id, "push", filepath, target]
run_cmd(cmd)
- def process(self, path: str, args: list[str] = [], stderr: int = subprocess.DEVNULL) -> subprocess.Popen:
- logger.debug(f'run process: {path}, args: {args}')
- cmd = [self.adb_bin, '-s', self.device_id, 'shell', path] + args
- return subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=stderr)
+ def process(
+ self, path: str, args: list[str] = [], stderr: int = subprocess.DEVNULL
+ ) -> subprocess.Popen:
+ logger.debug(f"run process: {path}, args: {args}")
+ cmd = [self.adb_bin, "-s", self.device_id, "shell", path] + args
+ return subprocess.Popen(
+ cmd,
+ stdout=subprocess.DEVNULL,
+ stderr=stderr,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ )
def push(self, target_path: str, target: bytes) -> None:
- """ push file into device """
+ """push file into device"""
self.session().push(target_path, target)
def stream(self, cmd: str) -> Socket:
- """ run adb command, return socket """
+ """run adb command, return socket"""
return self.session().request(cmd, True).sock
-
+
def stream_shell(self, cmd: str) -> Socket:
- """ run adb shell command, return socket """
- return self.stream('shell:' + cmd)
+ """run adb shell command, return socket"""
+ return self.stream("shell:" + cmd)
def android_version(self) -> str:
- """ get android_version """
- return self.cmd_shell('getprop ro.build.version.release', True)
+ """get android_version"""
+ return self.cmd_shell("getprop ro.build.version.release", True)
diff --git a/arknights_mower/utils/device/adb_client/session.py b/arknights_mower/utils/device/adb_client/session.py
index 406fb9cbf..d8cd57672 100644
--- a/arknights_mower/utils/device/adb_client/session.py
+++ b/arknights_mower/utils/device/adb_client/session.py
@@ -4,21 +4,16 @@
import struct
import time
-from ... import config
-from ...log import logger
-from .socket import Socket
+from arknights_mower.utils.device.adb_client.socket import Socket
+from arknights_mower.utils.log import logger
-class Session(object):
- """ Session between ADB client and ADB server """
+class Session:
+ """Session between ADB client and ADB server"""
- def __init__(self, server: tuple[str, int] = None, timeout: int = None) -> None:
- if server is None:
- server = (config.ADB_SERVER_IP, config.ADB_SERVER_PORT)
- if timeout is None:
- timeout = config.ADB_SERVER_TIMEOUT
- self.server = server
- self.timeout = timeout
+ def __init__(self):
+ self.server = "127.0.0.1", 5037
+ self.timeout = 5
self.device_id = None
self.sock = Socket(self.server, self.timeout)
@@ -29,103 +24,99 @@ def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
pass
def request(self, cmd: str, reconnect: bool = False) -> Session:
- """ make a service request to ADB server, consult ADB sources for available services """
+ """make a service request to ADB server, consult ADB sources for available services"""
cmdbytes = cmd.encode()
- data = b'%04X%b' % (len(cmdbytes), cmdbytes)
- while self.timeout <= 60:
+ data = b"%04X%b" % (len(cmdbytes), cmdbytes)
+ while self.timeout <= 10:
try:
self.sock.send(data).check_okay()
return self
except socket.timeout:
- logger.warning(f'socket.timeout: {self.timeout}s, +5s')
+ logger.warning(f"socket.timeout: {self.timeout}s, +5s")
self.timeout += 5
self.sock = Socket(self.server, self.timeout)
if reconnect:
self.device(self.device_id)
- raise socket.timeout(f'server: {self.server}')
+ raise socket.timeout(f"server: {self.server}")
def response(self, recv_all: bool = False) -> bytes:
- """ receive response """
+ """receive response"""
if recv_all:
return self.sock.recv_all()
else:
return self.sock.recv_response()
def exec(self, cmd: str) -> bytes:
- """ exec: cmd """
+ """exec: cmd"""
if len(cmd) == 0:
- raise ValueError('no command specified for exec')
- return self.request('exec:' + cmd, True).response(True)
+ raise ValueError("no command specified for exec")
+ return self.request("exec:" + cmd, True).response(True)
def shell(self, cmd: str) -> bytes:
- """ shell: cmd """
+ """shell: cmd"""
if len(cmd) == 0:
- raise ValueError('no command specified for shell')
- return self.request('shell:' + cmd, True).response(True)
+ raise ValueError("no command specified for shell")
+ return self.request("shell:" + cmd, True).response(True)
def host(self, cmd: str) -> bytes:
- """ host: cmd """
+ """host: cmd"""
if len(cmd) == 0:
- raise ValueError('no command specified for host')
- return self.request('host:' + cmd, True).response()
+ raise ValueError("no command specified for host")
+ return self.request("host:" + cmd, True).response()
def run(self, cmd: str, recv_all: bool = False) -> bytes:
- """ run command """
+ """run command"""
if len(cmd) == 0:
- raise ValueError('no command specified')
+ raise ValueError("no command specified")
return self.request(cmd, True).response(recv_all)
def device(self, device_id: str = None) -> Session:
- """ switch to a device """
+ """switch to a device"""
self.device_id = device_id
if device_id is None:
- return self.request('host:transport-any')
+ return self.request("host:transport-any")
else:
- return self.request('host:transport:' + device_id)
+ return self.request("host:transport:" + device_id)
def connect(self, device: str, throw_error: bool = False) -> None:
- """ connect device [ip:port] """
- if len(device.split(':')) != 2 or not device.split(':')[-1].isdigit():
- raise ValueError('wrong format of device address')
- resp = self.request(f'host:connect:{device}').response()
- logger.debug(f'adb connect {device}: {repr(resp)}')
- if throw_error and (b'unable' in resp or b'cannot' in resp):
+ """connect device [ip:port]"""
+ resp = self.request(f"host:connect:{device}").response()
+ logger.debug(f"adb connect {device}: {repr(resp)}")
+ if throw_error and (b"unable" in resp or b"cannot" in resp):
raise RuntimeError(repr(resp))
def disconnect(self, device: str, throw_error: bool = False) -> None:
- """ disconnect device [ip:port] """
- if len(device.split(':')) != 2 or not device.split(':')[-1].isdigit():
- raise ValueError('wrong format of device address')
- resp = self.request(f'host:disconnect:{device}').response()
- logger.debug(f'adb disconnect {device}: {repr(resp)}')
- if throw_error and (b'unable' in resp or b'cannot' in resp):
+ """disconnect device [ip:port]"""
+ resp = self.request(f"host:disconnect:{device}").response()
+ logger.debug(f"adb disconnect {device}: {repr(resp)}")
+ if throw_error and (b"unable" in resp or b"cannot" in resp):
raise RuntimeError(repr(resp))
def devices_list(self) -> list[tuple[str, str]]:
- """ returns list of devices that the adb server knows """
- resp = self.request('host:devices').response().decode(errors='ignore')
- devices = [tuple(line.split('\t')) for line in resp.splitlines()]
+ """returns list of devices that the adb server knows"""
+ resp = self.request("host:devices").response().decode(errors="ignore")
+ devices = [tuple(line.split("\t")) for line in resp.splitlines()]
logger.debug(devices)
return devices
def push(self, target_path: str, target: bytes, mode=0o100755, mtime: int = None):
- """ push data to device """
- self.request('sync:', True)
- request = b'%s,%d' % (target_path.encode(), mode)
- self.sock.send(b'SEND' + struct.pack(' None:
- logger.debug(f'server: {server}, timeout: {timeout}')
+ logger.debug(f"server: {server}, timeout: {timeout}")
try:
self.sock = None
self.sock = socket.create_connection(server, timeout=timeout)
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
except ConnectionRefusedError as e:
- logger.error(f'ConnectionRefusedError: {server}')
+ logger.error(f"ConnectionRefusedError: {server}")
raise e
def __enter__(self) -> Socket:
@@ -28,7 +28,7 @@ def __del__(self) -> None:
self.close()
def close(self) -> None:
- """ close socket """
+ """close socket"""
self.sock and self.sock.close()
self.sock = None
@@ -49,7 +49,7 @@ def recv_all(self, chunklen: int = 65536) -> bytes:
view = view[rcvlen:]
pos += rcvlen
data.append(buf[:pos])
- return b''.join(data)
+ return b"".join(data)
def recv_exactly(self, len: int) -> bytes:
buf = bytearray(len)
@@ -62,32 +62,32 @@ def recv_exactly(self, len: int) -> bytes:
view = view[rcvlen:]
pos += rcvlen
if pos != len:
- raise EOFError('recv_exactly %d bytes failed' % len)
+ raise EOFError("recv_exactly %d bytes failed" % len)
return bytes(buf)
def recv_response(self) -> bytes:
- """ read a chunk of length indicated by 4 hex digits """
+ """read a chunk of length indicated by 4 hex digits"""
len = int(self.recv_exactly(4), 16)
if len == 0:
- return b''
+ return b""
return self.recv_exactly(len)
def check_okay(self) -> None:
- """ check if first 4 bytes is "OKAY" """
+ """check if first 4 bytes is "OKAY" """
result = self.recv_exactly(4)
- if result != b'OKAY':
+ if result != b"OKAY":
raise ConnectionError(self.recv_response())
def recv(self, len: int) -> bytes:
return self.sock.recv(len)
def send(self, data: bytes) -> Socket:
- """ send data to server """
+ """send data to server"""
self.sock.send(data)
return self
def sendall(self, data: bytes) -> Socket:
- """ send data to server """
+ """send data to server"""
self.sock.sendall(data)
return self
diff --git a/arknights_mower/utils/device/adb_client/utils.py b/arknights_mower/utils/device/adb_client/utils.py
index 25882a7b6..5caf9fc6f 100644
--- a/arknights_mower/utils/device/adb_client/utils.py
+++ b/arknights_mower/utils/device/adb_client/utils.py
@@ -1,46 +1,21 @@
-from __future__ import annotations
-
-import shutil
import subprocess
from typing import Union
-from .... import __system__
-from ... import config
-from ...log import logger
-from ..utils import download_file
-
-ADB_BUILDIN_URL = 'https://oss.nano.ac/arknights_mower/adb-binaries'
-ADB_BUILDIN_FILELIST = {
- 'linux': ['adb'],
- 'windows': ['adb.exe', 'AdbWinApi.dll', 'AdbWinUsbApi.dll'],
- 'darwin': ['adb'],
-}
-
-
-def adb_buildin() -> None:
- """ download adb_bin """
- folder = config.init_adb_buildin()
- folder.mkdir(exist_ok=True, parents=True)
- if __system__ not in ADB_BUILDIN_FILELIST.keys():
- raise NotImplementedError(f'Unknown system: {__system__}')
- for file in ADB_BUILDIN_FILELIST[__system__]:
- target_path = folder / file
- if not target_path.exists():
- url = f'{ADB_BUILDIN_URL}/{__system__}/{file}'
- logger.debug(f'adb_buildin: {url}')
- tmp_path = download_file(url)
- shutil.copy(tmp_path, str(target_path))
- config.ADB_BUILDIN = folder / ADB_BUILDIN_FILELIST[__system__][0]
- config.ADB_BUILDIN.chmod(0o744)
+from arknights_mower import __system__
+from arknights_mower.utils.log import logger
def run_cmd(cmd: list[str], decode: bool = False) -> Union[bytes, str]:
logger.debug(f"run command: {cmd}")
try:
- r = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ r = subprocess.check_output(
+ cmd,
+ stderr=subprocess.STDOUT,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ )
except subprocess.CalledProcessError as e:
logger.debug(e.output)
raise e
if decode:
- return r.decode('utf8')
+ return r.decode("utf8")
return r
diff --git a/arknights_mower/utils/device/device.py b/arknights_mower/utils/device/device.py
index f406c57f6..0841bcc17 100644
--- a/arknights_mower/utils/device/device.py
+++ b/arknights_mower/utils/device/device.py
@@ -1,69 +1,96 @@
from __future__ import annotations
+import gzip
+import subprocess
import time
+from datetime import datetime, timedelta
from typing import Optional
-from .. import config
-from ..log import logger, save_screenshot
-from .adb_client import ADBClient
-from .minitouch import MiniTouch
-from .scrcpy import Scrcpy
+import cv2
+import numpy as np
+from arknights_mower import __rootdir__, __system__
+from arknights_mower.utils import config
+from arknights_mower.utils.csleep import MowerExit, csleep
+from arknights_mower.utils.device.adb_client.core import Client as ADBClient
+from arknights_mower.utils.device.adb_client.session import Session
+from arknights_mower.utils.device.maatouch import MaaTouch
+from arknights_mower.utils.device.scrcpy import Scrcpy
+from arknights_mower.utils.image import bytes2img, img2bytes
+from arknights_mower.utils.log import logger, save_screenshot
+from arknights_mower.utils.network import get_new_port, is_port_in_use
+from arknights_mower.utils.simulator import restart_simulator
-class Device(object):
- """ Android Device """
- class Control(object):
- """ Android Device Control """
+class Device:
+ """Android Device"""
- def __init__(self, device: Device, client: ADBClient = None, touch_device: str = None) -> None:
+ class Control:
+ """Android Device Control"""
+
+ def __init__(
+ self, device: Device, client: ADBClient = None, touch_device: str = None
+ ) -> None:
self.device = device
- self.minitouch = None
+ self.maatouch = None
self.scrcpy = None
- if config.ADB_CONTROL_CLIENT == 'minitouch':
- self.minitouch = MiniTouch(client, touch_device)
- elif config.ADB_CONTROL_CLIENT == 'scrcpy':
- self.scrcpy = Scrcpy(client)
+ if config.conf.touch_method == "maatouch":
+ self.maatouch = MaaTouch(client)
else:
- # MiniTouch does not support Android 10+
- if int(client.android_version().split('.')[0]) < 10:
- self.minitouch = MiniTouch(client, touch_device)
- else:
- self.scrcpy = Scrcpy(client)
+ self.scrcpy = Scrcpy(client)
def tap(self, point: tuple[int, int]) -> None:
- if self.minitouch:
- self.minitouch.tap([point], self.device.display_frames())
+ if self.maatouch:
+ self.maatouch.tap([point], self.device.display_frames())
elif self.scrcpy:
self.scrcpy.tap(point[0], point[1])
else:
raise NotImplementedError
- def swipe(self, start: tuple[int, int], end: tuple[int, int], duration: int) -> None:
- if self.minitouch:
- self.minitouch.swipe(
- [start, end], self.device.display_frames(), duration=duration)
+ def swipe(
+ self, start: tuple[int, int], end: tuple[int, int], duration: int
+ ) -> None:
+ if self.maatouch:
+ self.maatouch.swipe(
+ [start, end], self.device.display_frames(), duration=duration
+ )
elif self.scrcpy:
- self.scrcpy.swipe(
- start[0], start[1], end[0], end[1], duration / 1000)
+ self.scrcpy.swipe(start[0], start[1], end[0], end[1], duration / 1000)
else:
raise NotImplementedError
- def swipe_ext(self, points: list[tuple[int, int]], durations: list[int], up_wait: int) -> None:
- if self.minitouch:
- self.minitouch.swipe(
- points, self.device.display_frames(), duration=durations, up_wait=up_wait)
+ def swipe_ext(
+ self, points: list[tuple[int, int]], durations: list[int], up_wait: int
+ ) -> None:
+ if self.maatouch:
+ self.maatouch.swipe(
+ points,
+ self.device.display_frames(),
+ duration=durations,
+ up_wait=up_wait,
+ )
elif self.scrcpy:
total = len(durations)
- for idx, (S, E, D) in enumerate(zip(points[:-1], points[1:], durations)):
- self.scrcpy.swipe(S[0], S[1], E[0], E[1], D / 1000,
- up_wait / 1000 if idx == total-1 else 0,
- fall=idx == 0, lift=idx == total-1)
+ for idx, (S, E, D) in enumerate(
+ zip(points[:-1], points[1:], durations)
+ ):
+ self.scrcpy.swipe(
+ S[0],
+ S[1],
+ E[0],
+ E[1],
+ D / 1000,
+ up_wait / 1000 if idx == total - 1 else 0,
+ fall=idx == 0,
+ lift=idx == total - 1,
+ )
else:
raise NotImplementedError
- def __init__(self, device_id: str = None, connect: str = None, touch_device: str = None) -> None:
+ def __init__(
+ self, device_id: str = None, connect: str = None, touch_device: str = None
+ ) -> None:
self.device_id = device_id
self.connect = connect
self.touch_device = touch_device
@@ -78,71 +105,290 @@ def start(self) -> None:
def run(self, cmd: str) -> Optional[bytes]:
return self.client.run(cmd)
- def launch(self, app: str) -> None:
- """ launch the application """
- self.run(f'am start -n {app}')
+ def launch(self) -> None:
+ """launch the application"""
+ logger.info("明日方舟,启动!")
- def exit(self, app: str) -> None:
- """ launch the application """
- self.run(f'am force-stop {app}')
+ tap = config.conf.tap_to_launch_game.enable
+ x = config.conf.tap_to_launch_game.x
+ y = config.conf.tap_to_launch_game.y
+
+ if tap:
+ self.run(f"input tap {x} {y}")
+ else:
+ self.run(f"am start -n {config.conf.APPNAME}/{config.APP_ACTIVITY_NAME}")
+
+ def exit(self) -> None:
+ """exit the application"""
+ logger.info("退出游戏")
+ self.run(f"am force-stop {config.conf.APPNAME}")
def send_keyevent(self, keycode: int) -> None:
- """ send a key event """
- logger.debug(f'keyevent: {keycode}')
- command = f'input keyevent {keycode}'
+ """send a key event"""
+ logger.debug(f"keyevent: {keycode}")
+ command = f"input keyevent {keycode}"
self.run(command)
def send_text(self, text: str) -> None:
- """ send a text """
- logger.debug(f'text: {repr(text)}')
+ """send a text"""
+ logger.debug(f"text: {repr(text)}")
text = text.replace('"', '\\"')
command = f'input text "{text}"'
self.run(command)
- def screencap(self, save: bool = False) -> bytes:
- """ get a screencap """
- command = 'screencap -p'
- screencap = self.run(command)
- if save:
- save_screenshot(screencap)
- return screencap
+ def get_droidcast_classpath(self) -> str | None:
+ # TODO: 退出时(并非结束mower线程时)关闭DroidCast进程、取消ADB转发
+ try:
+ out = self.client.cmd_shell("pm path com.rayworks.droidcast", decode=True)
+ except Exception:
+ logger.exception("无法获取CLASSPATH")
+ return None
+ prefix = "package:"
+ postfix = ".apk"
+ beg = out.index(prefix, 0)
+ end = out.rfind(postfix)
+ class_path = out[beg + len(prefix) : (end + len(postfix))].strip()
+ class_path = "CLASSPATH=" + class_path
+ logger.info(f"成功获取CLASSPATH:{class_path}")
+ return class_path
+
+ def start_droidcast(self) -> bool:
+ class_path = self.get_droidcast_classpath()
+ if not class_path:
+ logger.info("安装DroidCast")
+ apk_path = f"{__rootdir__}/vendor/droidcast/DroidCast-debug-1.2.1.apk"
+ out = self.client.cmd(["install", apk_path], decode=True)
+ if "Success" in out:
+ logger.info("DroidCast安装完成,获取CLASSPATH")
+ else:
+ logger.error(f"DroidCast安装失败:{out}")
+ return False
+ class_path = self.get_droidcast_classpath()
+ if not class_path:
+ logger.error(f"无法获取CLASSPATH:{out}")
+ return False
+ port = config.droidcast.port
+ if port != 0 and is_port_in_use(port):
+ try:
+ occupied_by_adb_forward = False
+ forward_list = self.client.cmd("forward --list", True).strip().split()
+ for host, pc_port, android_port in forward_list:
+ # 127.0.0.1:5555 tcp:60579 tcp:60579
+ if pc_port != android_port:
+ # 不是咱转发的,别乱动
+ continue
+ if pc_port == f"tcp:{port}":
+ occupied_by_adb_forward = True
+ break
+ if not occupied_by_adb_forward:
+ port = 0
+ except Exception as e:
+ logger.exception(e)
+ if port == 0:
+ port = get_new_port()
+ config.droidcast.port = port
+ logger.info(f"更新DroidCast端口为{port}")
+ else:
+ logger.info(f"保持DroidCast端口为{port}")
+ self.client.cmd(f"forward tcp:{port} tcp:{port}")
+ logger.info("ADB端口转发成功,启动DroidCast")
+ if config.droidcast.process is not None:
+ config.droidcast.process.terminate()
+ process = self.client.process(
+ class_path,
+ [
+ "app_process",
+ "/",
+ "com.rayworks.droidcast.Main",
+ f"--port={port}",
+ ],
+ )
+ config.droidcast.process = process
+ return True
+
+ def screencap(self) -> bytes:
+ start_time = datetime.now()
+ min_time = config.screenshot_time + timedelta(
+ milliseconds=config.conf.screenshot_interval
+ )
+ delta = (min_time - start_time).total_seconds()
+ if delta > 0:
+ time.sleep(delta)
+ start_time = min_time
+
+ if config.conf.droidcast.enable:
+ session = config.droidcast.session
+ while True:
+ try:
+ port = config.droidcast.port
+ url = f"http://127.0.0.1:{port}/screenshot"
+ logger.debug(f"GET {url}")
+ r = session.get(url)
+ img = bytes2img(r.content)
+ if config.conf.droidcast.rotate:
+ img = cv2.rotate(img, cv2.ROTATE_180)
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+ break
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+ self.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ self.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ self.control.scrcpy = Scrcpy(self.client)
+ elif config.conf.custom_screenshot.enable:
+ command = config.conf.custom_screenshot.command
+ while True:
+ try:
+ data = subprocess.check_output(
+ command,
+ shell=True,
+ creationflags=subprocess.CREATE_NO_WINDOW
+ if __system__ == "windows"
+ else 0,
+ )
+ break
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+ self.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.touch_method == "scrcpy":
+ self.control.scrcpy = Scrcpy(self.client)
+ img = bytes2img(data)
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+ else:
+ command = "screencap 2>/dev/null | gzip -1"
+ while True:
+ try:
+ resp = self.run(command)
+ break
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+ self.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.touch_method == "scrcpy":
+ self.control.scrcpy = Scrcpy(self.client)
+ data = gzip.decompress(resp)
+ array = np.frombuffer(data[-1920 * 1080 * 4 :], np.uint8).reshape(
+ 1080, 1920, 4
+ )
+ img = cv2.cvtColor(array, cv2.COLOR_RGBA2RGB)
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+
+ screencap = img2bytes(img)
+ save_screenshot(screencap)
+
+ stop_time = datetime.now()
+ config.screenshot_time = stop_time
+ interval = (stop_time - start_time).total_seconds() * 1000
+ if config.screenshot_avg is None:
+ config.screenshot_avg = interval
+ else:
+ config.screenshot_avg = config.screenshot_avg * 0.9 + interval * 0.1
+ if config.screenshot_count >= 100:
+ config.screenshot_count = 0
+ logger.info(
+ f"截图用时{interval:.0f}ms 平均用时{config.screenshot_avg:.0f}ms"
+ )
+ else:
+ config.screenshot_count += 1
+
+ return screencap, img, gray
def current_focus(self) -> str:
- """ detect current focus app """
- command = 'dumpsys window | grep mCurrentFocus'
- line = self.run(command).decode('utf8')
- return line.strip()[:-1].split(' ')[-1]
+ """detect current focus app"""
+ command = "dumpsys window | grep mCurrentFocus"
+ line = self.run(command).decode("utf8")
+ return line.strip()[:-1].split(" ")[-1]
def display_frames(self) -> tuple[int, int, int]:
- """ get display frames if in compatibility mode"""
+ """get display frames if in compatibility mode"""
if not config.MNT_COMPATIBILITY_MODE:
return None
- command = 'dumpsys window | grep DisplayFrames'
- line = self.run(command).decode('utf8')
+ command = "dumpsys window | grep DisplayFrames"
+ line = self.run(command).decode("utf8")
""" eg. DisplayFrames w=1920 h=1080 r=3 """
- res = line.strip().replace('=', ' ').split(' ')
+ res = line.strip().replace("=", " ").split(" ")
return int(res[2]), int(res[4]), int(res[6])
def tap(self, point: tuple[int, int]) -> None:
- """ tap """
- logger.debug(f'tap: {point}')
+ """tap"""
+ logger.debug(f"tap: {point}")
self.control.tap(point)
- def swipe(self, start: tuple[int, int], end: tuple[int, int], duration: int = 100) -> None:
- """ swipe """
- logger.debug(f'swipe: {start} -> {end}, duration={duration}')
+ def swipe(
+ self, start: tuple[int, int], end: tuple[int, int], duration: int = 100
+ ) -> None:
+ """swipe"""
+ logger.debug(f"swipe: {start} -> {end}, duration={duration}")
self.control.swipe(start, end, duration)
- def swipe_ext(self, points: list[tuple[int, int]], durations: list[int], up_wait: int = 500) -> None:
- """ swipe_ext """
+ def swipe_ext(
+ self, points: list[tuple[int, int]], durations: list[int], up_wait: int = 200
+ ) -> None:
+ """swipe_ext"""
logger.debug(
- f'swipe_ext: points={points}, durations={durations}, up_wait={up_wait}')
+ f"swipe_ext: points={points}, durations={durations}, up_wait={up_wait}"
+ )
self.control.swipe_ext(points, durations, up_wait)
- def check_current_focus(self):
- """ check if the application is in the foreground """
- if self.current_focus() != config.APPNAME:
- self.launch(config.APPNAME)
- # wait for app to finish launching
- time.sleep(10)
+ def check_current_focus(self) -> bool:
+ """check if the application is in the foreground"""
+ update = False
+ while True:
+ try:
+ focus = self.current_focus()
+ if focus not in [
+ f"{config.conf.APPNAME}/{config.APP_ACTIVITY_NAME}",
+ "com.hypergryph.arknights.bilibili/com.gsc.welcome.WelcomeActivity",
+ ]:
+ self.exit() # 防止应用卡死
+ self.launch()
+ csleep(10)
+ update = True
+ return update
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+ self.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ self.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ self.control.scrcpy = Scrcpy(self.client)
+ update = True
+
+ def check_resolution(self) -> bool:
+ """检查分辨率"""
+
+ good_resolution = ["1920x1080", "1080x1920"]
+
+ def match_resolution(resolution):
+ return any(g in resolution for g in good_resolution)
+
+ def show_error(resolution):
+ logger.error(f"Mower仅支持1920x1080分辨率,模拟器分辨率为{resolution}")
+
+ def extract_resolution(output_str):
+ return output_str.partition("size:")[2].strip()
+
+ output = self.client.cmd_shell("wm size", True)
+ logger.debug(output.strip())
+
+ physical_str, _, override_str = output.partition("Override")
+
+ if override_str:
+ if match_resolution(override_str):
+ return True
+ show_error(extract_resolution(override_str))
+ return False
+ if match_resolution(physical_str):
+ return True
+ show_error(extract_resolution(physical_str))
+ return False
diff --git a/arknights_mower/utils/device/maatouch/__init__.py b/arknights_mower/utils/device/maatouch/__init__.py
new file mode 100644
index 000000000..5daf8435e
--- /dev/null
+++ b/arknights_mower/utils/device/maatouch/__init__.py
@@ -0,0 +1,3 @@
+from arknights_mower.utils.device.maatouch.core import Client as MaaTouch
+
+__all__ = ["MaaTouch"]
diff --git a/arknights_mower/utils/device/maatouch/command.py b/arknights_mower/utils/device/maatouch/command.py
new file mode 100644
index 000000000..af6a681cb
--- /dev/null
+++ b/arknights_mower/utils/device/maatouch/command.py
@@ -0,0 +1,50 @@
+import time
+
+from arknights_mower.utils.device.maatouch.session import Session
+from arknights_mower.utils.log import logger
+
+DEFAULT_DELAY = 0.05
+
+
+class CommandBuilder:
+ """Build command str for minitouch"""
+
+ def __init__(self) -> None:
+ self.content = ""
+ self.delay = 0
+
+ def append(self, new_content: str) -> None:
+ self.content += new_content + "\n"
+
+ def commit(self) -> None:
+ """add minitouch command: 'c\n'"""
+ self.append("c")
+
+ def wait(self, ms: int) -> None:
+ """add minitouch command: 'w \n'"""
+ self.append(f"w {ms}")
+ self.delay += ms
+
+ def up(self, contact_id: int) -> None:
+ """add minitouch command: 'u \n'"""
+ self.append(f"u {contact_id}")
+
+ def down(self, contact_id: int, x: int, y: int, pressure: int) -> None:
+ """add minitouch command: 'd \n'"""
+ self.append(f"d {contact_id} {x} {y} {pressure}")
+
+ def move(self, contact_id: int, x: int, y: int, pressure: int) -> None:
+ """add minitouch command: 'm \n'"""
+ self.append(f"m {contact_id} {x} {y} {pressure}")
+
+ def publish(self, session: Session):
+ """apply current commands to device"""
+ self.commit()
+ logger.debug("send operation: %s" % self.content.replace("\n", "\\n"))
+ session.send(self.content)
+ time.sleep(self.delay / 1000 + DEFAULT_DELAY)
+ self.reset()
+
+ def reset(self):
+ """clear current commands"""
+ self.content = ""
diff --git a/arknights_mower/utils/device/maatouch/core.py b/arknights_mower/utils/device/maatouch/core.py
new file mode 100644
index 000000000..a432449ae
--- /dev/null
+++ b/arknights_mower/utils/device/maatouch/core.py
@@ -0,0 +1,214 @@
+from typing import Union
+
+from arknights_mower import __rootdir__
+from arknights_mower.utils import config
+from arknights_mower.utils.device.adb_client.core import Client as ADBClient
+from arknights_mower.utils.device.maatouch.command import CommandBuilder
+from arknights_mower.utils.device.maatouch.session import Session
+from arknights_mower.utils.log import logger
+
+MNT_PATH = "/data/local/tmp/maatouch"
+
+
+class Client:
+ """Use maatouch to control Android devices easily"""
+
+ def __init__(self, client: ADBClient) -> None:
+ self.client = client
+ self.start()
+
+ def start(self) -> None:
+ self.__install()
+
+ def __del__(self) -> None:
+ pass
+
+ def __install(self) -> None:
+ """install maatouch for android devices"""
+ if self.__is_mnt_existed():
+ logger.debug(f"maatouch already existed in {self.client.device_id}")
+ else:
+ self.__download_mnt()
+
+ def __is_mnt_existed(self) -> bool:
+ """check if maatouch is existed in the device"""
+ file_list = self.client.cmd_shell("ls /data/local/tmp", True)
+ return "maatouch" in file_list
+
+ def __download_mnt(self) -> None:
+ """download maatouch"""
+ mnt_path = f"{__rootdir__}/vendor/maatouch/maatouch"
+
+ # push and grant
+ self.client.cmd_push(mnt_path, MNT_PATH)
+ logger.info("maatouch already installed in {MNT_PATH}")
+
+ def check_adb_alive(self) -> bool:
+ """check if adb server alive"""
+ return self.client.check_server_alive()
+
+ def convert_coordinate(
+ self,
+ point: tuple[int, int],
+ display_frames: tuple[int, int, int],
+ max_x: int,
+ max_y: int,
+ ) -> tuple[int, int]:
+ """
+ check compatibility mode and convert coordinate
+ see details: https://github.com/Konano/arknights-mower/issues/85
+ """
+ if not config.MNT_COMPATIBILITY_MODE:
+ return point
+ x, y = point
+ w, h, r = display_frames
+ if r == 1:
+ return [(h - y) * max_x // h, x * max_y // w]
+ if r == 3:
+ return [y * max_x // h, (w - x) * max_y // w]
+ logger.debug(
+ f"warning: unexpected rotation parameter: display_frames({w}, {h}, {r})"
+ )
+ return point
+
+ def tap(
+ self,
+ points: list[tuple[int, int]],
+ display_frames: tuple[int, int, int],
+ pressure: int = 100,
+ duration: int = None,
+ lift: bool = True,
+ ) -> None:
+ """
+ tap on screen with pressure and duration
+
+ :param points: list[int], look like [(x1, y1), (x2, y2), ...]
+ :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
+ :param pressure: default to 100
+ :param duration: in milliseconds
+ :param lift: if True, "lift" the touch point
+ """
+ self.check_adb_alive()
+
+ builder = CommandBuilder()
+ points = [list(map(int, point)) for point in points]
+ with Session(self.client) as conn:
+ for id, point in enumerate(points):
+ x, y = self.convert_coordinate(
+ point, display_frames, int(conn.max_x), int(conn.max_y)
+ )
+ builder.down(id, x, y, pressure)
+ builder.commit()
+
+ if duration:
+ builder.wait(duration)
+ builder.commit()
+
+ if lift:
+ for id in range(len(points)):
+ builder.up(id)
+
+ builder.publish(conn)
+
+ def __swipe(
+ self,
+ points: list[tuple[int, int]],
+ display_frames: tuple[int, int, int],
+ pressure: int = 100,
+ duration: Union[list[int], int] = None,
+ up_wait: int = 0,
+ fall: bool = True,
+ lift: bool = True,
+ ) -> None:
+ """
+ swipe between points one by one, with pressure and duration
+
+ :param points: list, look like [(x1, y1), (x2, y2), ...]
+ :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
+ :param pressure: default to 100
+ :param duration: in milliseconds
+ :param up_wait: in milliseconds
+ :param fall: if True, "fall" the first touch point
+ :param lift: if True, "lift" the last touch point
+ """
+ self.check_adb_alive()
+
+ points = [list(map(int, point)) for point in points]
+ if not isinstance(duration, list):
+ duration = [duration] * (len(points) - 1)
+ assert len(duration) + 1 == len(points)
+
+ builder = CommandBuilder()
+ with Session(self.client) as conn:
+ if fall:
+ x, y = self.convert_coordinate(
+ points[0], display_frames, int(conn.max_x), int(conn.max_y)
+ )
+ builder.down(0, x, y, pressure)
+ builder.publish(conn)
+
+ for idx, point in enumerate(points[1:]):
+ x, y = self.convert_coordinate(
+ point, display_frames, int(conn.max_x), int(conn.max_y)
+ )
+ builder.move(0, x, y, pressure)
+ if duration[idx - 1]:
+ builder.wait(duration[idx - 1])
+ builder.commit()
+ builder.publish(conn)
+
+ if lift:
+ builder.up(0)
+ if up_wait:
+ builder.wait(up_wait)
+ builder.publish(conn)
+
+ def swipe(
+ self,
+ points: list[tuple[int, int]],
+ display_frames: tuple[int, int, int],
+ pressure: int = 100,
+ duration: Union[list[int], int] = None,
+ up_wait: int = 0,
+ part: int = 10,
+ fall: bool = True,
+ lift: bool = True,
+ ) -> None:
+ """
+ swipe between points one by one, with pressure and duration
+ it will split distance between points into pieces
+
+ :param points: list, look like [(x1, y1), (x2, y2), ...]
+ :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
+ :param pressure: default to 100
+ :param duration: in milliseconds
+ :param up_wait: in milliseconds
+ :param part: default to 10
+ :param fall: if True, "fall" the first touch point
+ :param lift: if True, "lift" the last touch point
+ """
+ points = [list(map(int, point)) for point in points]
+ if not isinstance(duration, list):
+ duration = [duration] * (len(points) - 1)
+ assert len(duration) + 1 == len(points)
+
+ new_points = [points[0]]
+ new_duration = []
+ for id in range(1, len(points)):
+ pre_point = points[id - 1]
+ cur_point = points[id]
+ offset = (
+ (cur_point[0] - pre_point[0]) // part,
+ (cur_point[1] - pre_point[1]) // part,
+ )
+ new_points += [
+ (pre_point[0] + i * offset[0], pre_point[1] + i * offset[1])
+ for i in range(1, part + 1)
+ ]
+ if duration[id - 1] is None:
+ new_duration += [None] * part
+ else:
+ new_duration += [duration[id - 1] // part] * part
+ self.__swipe(
+ new_points, display_frames, pressure, new_duration, up_wait, fall, lift
+ )
diff --git a/arknights_mower/utils/device/maatouch/session.py b/arknights_mower/utils/device/maatouch/session.py
new file mode 100644
index 000000000..20a4f52cb
--- /dev/null
+++ b/arknights_mower/utils/device/maatouch/session.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+import subprocess
+
+from arknights_mower import __system__
+from arknights_mower.utils.device.adb_client.core import Client as ADBClient
+from arknights_mower.utils.log import logger
+
+
+class Session:
+ def __init__(self, client: ADBClient) -> None:
+ self.process = subprocess.Popen(
+ [
+ client.adb_bin,
+ "-s",
+ client.device_id,
+ "shell",
+ "CLASSPATH=/data/local/tmp/maatouch",
+ "app_process",
+ "/",
+ "com.shxyke.MaaTouch.App",
+ ],
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ text=True,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ )
+
+ # ^
+ _, max_contacts, max_x, max_y, max_pressure, *_ = (
+ self.process.stdout.readline().strip().split(" ")
+ )
+ self.max_contacts = max_contacts
+ self.max_x = max_x
+ self.max_y = max_y
+ self.max_pressure = max_pressure
+
+ # $
+ _, pid = self.process.stdout.readline().strip().split(" ")
+ self.pid = pid
+
+ logger.debug(f"maatouch running, pid: {self.pid}")
+ logger.debug(
+ f"max_contact: {max_contacts}; max_x: {max_x}; max_y: {max_y}; max_pressure: {max_pressure}"
+ )
+
+ def __enter__(self) -> Session:
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
+ self.process.terminate()
+
+ def send(self, content: str):
+ self.process.stdin.write(content)
+ self.process.stdin.flush()
diff --git a/arknights_mower/utils/device/minitouch/__init__.py b/arknights_mower/utils/device/minitouch/__init__.py
deleted file mode 100644
index 6d66505bf..000000000
--- a/arknights_mower/utils/device/minitouch/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .core import Client as MiniTouch
diff --git a/arknights_mower/utils/device/minitouch/command.py b/arknights_mower/utils/device/minitouch/command.py
deleted file mode 100644
index f6ac23a97..000000000
--- a/arknights_mower/utils/device/minitouch/command.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from __future__ import annotations
-
-import time
-
-from ...log import logger
-from .session import Session
-
-DEFAULT_DELAY = 0.05
-
-
-class CommandBuilder(object):
- """ Build command str for minitouch """
-
- def __init__(self) -> None:
- self.content = ''
- self.delay = 0
-
- def append(self, new_content: str) -> None:
- self.content += new_content + '\n'
-
- def commit(self) -> None:
- """ add minitouch command: 'c\n' """
- self.append('c')
-
- def wait(self, ms: int) -> None:
- """ add minitouch command: 'w \n' """
- self.append(f'w {ms}')
- self.delay += ms
-
- def up(self, contact_id: int) -> None:
- """ add minitouch command: 'u \n' """
- self.append(f'u {contact_id}')
-
- def down(self, contact_id: int, x: int, y: int, pressure: int) -> None:
- """ add minitouch command: 'd \n' """
- self.append(f'd {contact_id} {x} {y} {pressure}')
-
- def move(self, contact_id: int, x: int, y: int, pressure: int) -> None:
- """ add minitouch command: 'm \n' """
- self.append(f'm {contact_id} {x} {y} {pressure}')
-
- def publish(self, session: Session):
- """ apply current commands to device """
- self.commit()
- logger.debug('send operation: %s' % self.content.replace('\n', '\\n'))
- session.send(self.content)
- time.sleep(self.delay / 1000 + DEFAULT_DELAY)
- self.reset()
-
- def reset(self):
- """ clear current commands """
- self.content = ''
diff --git a/arknights_mower/utils/device/minitouch/core.py b/arknights_mower/utils/device/minitouch/core.py
deleted file mode 100644
index aeaba499f..000000000
--- a/arknights_mower/utils/device/minitouch/core.py
+++ /dev/null
@@ -1,254 +0,0 @@
-from __future__ import annotations
-
-import os
-import time
-# import random
-from typing import Union
-
-from ... import config
-from ...log import log_sync, logger
-from ..adb_client import ADBClient
-from ..utils import download_file
-from .command import CommandBuilder
-from .session import Session
-
-# MNT_PREBUILT_URL = 'https://github.com/williamfzc/stf-binaries/raw/master/node_modules/minitouch-prebuilt/prebuilt'
-MNT_PREBUILT_URL = 'https://oss.nano.ac/arknights_mower/minitouch'
-MNT_PATH = '/data/local/tmp/minitouch'
-
-
-class Client(object):
- """ Use minitouch to control Android devices easily """
-
- def __init__(self, client: ADBClient, touch_device: str = config.MNT_TOUCH_DEVICE) -> None:
- self.client = client
- self.touch_device = touch_device
- self.process = None
- self.start()
-
- def start(self) -> None:
- self.__install()
- self.__server()
-
- def __del__(self) -> None:
- self.stop()
-
- def stop(self) -> None:
- self.__server_stop()
-
- def __install(self) -> None:
- """ install minitouch for android devices """
- self.abi = self.__get_abi()
- if self.__is_mnt_existed():
- logger.debug(
- f'minitouch already existed in {self.client.device_id}')
- else:
- self.__download_mnt()
-
- def __get_abi(self) -> str:
- """ query device ABI """
- abi = self.client.cmd_shell('getprop ro.product.cpu.abi', True).strip()
- logger.debug(f'device_abi: {abi}')
- return abi
-
- def __is_mnt_existed(self) -> bool:
- """ check if minitouch is existed in the device """
- file_list = self.client.cmd_shell('ls /data/local/tmp', True)
- return 'minitouch' in file_list
-
- def __download_mnt(self) -> None:
- """ download minitouch """
- url = f'{MNT_PREBUILT_URL}/{self.abi}/bin/minitouch'
- logger.info(f'minitouch url: {url}')
- mnt_path = download_file(url)
-
- # push and grant
- self.client.cmd_push(mnt_path, MNT_PATH)
- self.client.cmd_shell(f'chmod 777 {MNT_PATH}')
- logger.info('minitouch already installed in {MNT_PATH}')
-
- # remove temp
- os.remove(mnt_path)
-
- def __server(self) -> None:
- """ execute minitouch with adb shell """
- # self.port = self.__get_port()
- self.port = config.MNT_PORT
- self.__forward_port()
- self.process = None
- r, self.stderr = os.pipe()
- log_sync('minitouch', r).start()
- self.__start_mnt()
-
- # make sure minitouch is up
- time.sleep(1)
- if not self.check_mnt_alive(False):
- raise RuntimeError('minitouch did not work. see https://github.com/Konano/arknights-mower/issues/82')
-
- def __server_stop(self) -> None:
- """ stop minitouch """
- self.process and self.process.kill()
-
- # def __get_port(cls) -> int:
- # """ get a random port from port set """
- # while True:
- # port = random.choice(list(range(20000, 21000)))
- # if is_port_using(DEFAULT_HOST, port):
- # return port
-
- def __forward_port(self) -> None:
- """ allow pc access minitouch with port """
- output = self.client.cmd(
- f'forward tcp:{self.port} localabstract:minitouch')
- logger.debug(f'output: {output}')
-
- def __start_mnt(self) -> None:
- """ fork a process to start minitouch on android """
- if self.touch_device is None:
- self.process = self.client.process('/data/local/tmp/minitouch', [], self.stderr)
- else:
- self.process = self.client.process('/data/local/tmp/minitouch', ['-d', self.touch_device], self.stderr)
-
- def check_mnt_alive(self, restart: bool = True) -> bool:
- """ check if minitouch process alive """
- if self.process and self.process.poll() is None:
- return True
- elif restart:
- self.__server_stop()
- self.__forward_port()
- self.__start_mnt()
- time.sleep(1)
- if not (self.process and self.process.poll() is None):
- raise RuntimeError('minitouch did not work. see https://github.com/Konano/arknights-mower/issues/82')
- return True
- return False
-
- def check_adb_alive(self) -> bool:
- """ check if adb server alive """
- return self.client.check_server_alive()
-
- def convert_coordinate(self, point: tuple[int, int], display_frames: tuple[int, int, int], max_x: int, max_y: int) -> tuple[int, int]:
- """
- check compatibility mode and convert coordinate
- see details: https://github.com/Konano/arknights-mower/issues/85
- """
- if not config.MNT_COMPATIBILITY_MODE:
- return point
- x, y = point
- w, h, r = display_frames
- if r == 1:
- return [(h - y) * max_x // h, x * max_y // w]
- if r == 3:
- return [y * max_x // h, (w - x) * max_y // w]
- logger.debug(f'warning: unexpected rotation parameter: display_frames({w}, {h}, {r})')
- return point
-
- def tap(self, points: list[tuple[int, int]], display_frames: tuple[int, int, int], pressure: int = 100, duration: int = None, lift: bool = True) -> None:
- """
- tap on screen with pressure and duration
-
- :param points: list[int], look like [(x1, y1), (x2, y2), ...]
- :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
- :param pressure: default to 100
- :param duration: in milliseconds
- :param lift: if True, "lift" the touch point
- """
- self.check_adb_alive()
- self.check_mnt_alive()
-
- builder = CommandBuilder()
- points = [list(map(int, point)) for point in points]
- with Session(self.port) as conn:
- for id, point in enumerate(points):
- x, y = self.convert_coordinate(point, display_frames, int(conn.max_x), int(conn.max_y))
- builder.down(id, x, y, pressure)
- builder.commit()
-
- if duration:
- builder.wait(duration)
- builder.commit()
-
- if lift:
- for id in range(len(points)):
- builder.up(id)
-
- builder.publish(conn)
-
- def __swipe(self, points: list[tuple[int, int]], display_frames: tuple[int, int, int], pressure: int = 100, duration: Union[list[int], int] = None, up_wait: int = 0, fall: bool = True, lift: bool = True) -> None:
- """
- swipe between points one by one, with pressure and duration
-
- :param points: list, look like [(x1, y1), (x2, y2), ...]
- :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
- :param pressure: default to 100
- :param duration: in milliseconds
- :param up_wait: in milliseconds
- :param fall: if True, "fall" the first touch point
- :param lift: if True, "lift" the last touch point
- """
- self.check_adb_alive()
- self.check_mnt_alive()
-
- points = [list(map(int, point)) for point in points]
- if not isinstance(duration, list):
- duration = [duration] * (len(points) - 1)
- assert len(duration) + 1 == len(points)
-
- builder = CommandBuilder()
- with Session(self.port) as conn:
- if fall:
- x, y = self.convert_coordinate(points[0], display_frames, int(conn.max_x), int(conn.max_y))
- builder.down(0, x, y, pressure)
- builder.publish(conn)
-
- for idx, point in enumerate(points[1:]):
- x, y = self.convert_coordinate(point, display_frames, int(conn.max_x), int(conn.max_y))
- builder.move(0, x, y, pressure)
- if duration[idx-1]:
- builder.wait(duration[idx-1])
- builder.commit()
- builder.publish(conn)
-
- if lift:
- builder.up(0)
- if up_wait:
- builder.wait(up_wait)
- builder.publish(conn)
-
- def swipe(self, points: list[tuple[int, int]], display_frames: tuple[int, int, int], pressure: int = 100, duration: Union[list[int], int] = None, up_wait: int = 0, part: int = 10, fall: bool = True, lift: bool = True) -> None:
- """
- swipe between points one by one, with pressure and duration
- it will split distance between points into pieces
-
- :param points: list, look like [(x1, y1), (x2, y2), ...]
- :param display_frames: tuple[int, int, int], which means [weight, high, rotation] by "adb shell dumpsys window | grep DisplayFrames"
- :param pressure: default to 100
- :param duration: in milliseconds
- :param up_wait: in milliseconds
- :param part: default to 10
- :param fall: if True, "fall" the first touch point
- :param lift: if True, "lift" the last touch point
- """
- points = [list(map(int, point)) for point in points]
- if not isinstance(duration, list):
- duration = [duration] * (len(points) - 1)
- assert len(duration) + 1 == len(points)
-
- new_points = [points[0]]
- new_duration = []
- for id in range(1, len(points)):
- pre_point = points[id-1]
- cur_point = points[id]
- offset = (
- (cur_point[0] - pre_point[0]) // part,
- (cur_point[1] - pre_point[1]) // part,
- )
- new_points += [
- (pre_point[0] + i * offset[0], pre_point[1] + i * offset[1])
- for i in range(1, part+1)
- ]
- if duration[id-1] is None:
- new_duration += [None] * part
- else:
- new_duration += [duration[id-1] // part] * part
- self.__swipe(new_points, display_frames, pressure, new_duration, up_wait, fall, lift)
diff --git a/arknights_mower/utils/device/minitouch/session.py b/arknights_mower/utils/device/minitouch/session.py
deleted file mode 100644
index a86263375..000000000
--- a/arknights_mower/utils/device/minitouch/session.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-import socket
-
-from ...log import logger
-
-DEFAULT_HOST = '127.0.0.1'
-
-
-class Session(object):
- """ manage socket connections between PC and Android """
-
- def __init__(self, port: int, buf_size: int = 0) -> None:
- self.port = port
- self.buf_size = buf_size
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect((DEFAULT_HOST, port))
- socket_out = self.sock.makefile()
-
- # v
- # protocol version, usually it is 1. needn't use this
- socket_out.readline()
-
- # ^
- _, max_contacts, max_x, max_y, max_pressure, *_ = (
- socket_out.readline().strip().split(' '))
- self.max_contacts = max_contacts
- self.max_x = max_x
- self.max_y = max_y
- self.max_pressure = max_pressure
-
- # $
- _, pid = socket_out.readline().strip().split(' ')
- self.pid = pid
-
- logger.debug(
- f'minitouch running on port: {self.port}, pid: {self.pid}')
- logger.debug(
- f'max_contact: {max_contacts}; max_x: {max_x}; max_y: {max_y}; max_pressure: {max_pressure}')
-
- def __enter__(self) -> Session:
- return self
-
- def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
- pass
-
- def __del__(self) -> None:
- self.close()
-
- def close(self) -> None:
- """ cancel connection """
- self.sock and self.sock.close()
- self.sock = None
-
- def send(self, content: str) -> bytes:
- content = content.encode('utf8')
- self.sock.sendall(content)
- return self.sock.recv(self.buf_size)
diff --git a/arknights_mower/utils/device/scrcpy/__init__.py b/arknights_mower/utils/device/scrcpy/__init__.py
index 12edc42b2..8f7857dcb 100644
--- a/arknights_mower/utils/device/scrcpy/__init__.py
+++ b/arknights_mower/utils/device/scrcpy/__init__.py
@@ -1 +1,3 @@
from .core import Client as Scrcpy
+
+__all__ = ["Scrcpy"]
diff --git a/arknights_mower/utils/device/scrcpy/core.py b/arknights_mower/utils/device/scrcpy/core.py
index 9bd4fe830..8c319fe5f 100644
--- a/arknights_mower/utils/device/scrcpy/core.py
+++ b/arknights_mower/utils/device/scrcpy/core.py
@@ -10,14 +10,14 @@
import numpy as np
-from .... import __rootdir__
-from ...log import logger
-from ..adb_client import ADBClient
-from ..adb_client.socket import Socket
-from . import const
-from .control import ControlSender
+from arknights_mower import __rootdir__
+from arknights_mower.utils.device.adb_client.core import Client as ADBClient
+from arknights_mower.utils.device.adb_client.socket import Socket
+from arknights_mower.utils.device.scrcpy import const
+from arknights_mower.utils.device.scrcpy.control import ControlSender
+from arknights_mower.utils.log import logger
-SCR_PATH = '/data/local/tmp/minitouch'
+SCR_PATH = "/data/local/tmp/minitouch"
class Client:
@@ -81,23 +81,28 @@ def __start_server(self) -> None:
"""
Start server and get the connection
"""
- cmdline = f'CLASSPATH={SCR_PATH} app_process /data/local/tmp com.genymobile.scrcpy.Server 1.21 log_level=verbose control=true tunnel_forward=true'
+ cmdline = f"CLASSPATH={SCR_PATH} app_process /data/local/tmp com.genymobile.scrcpy.Server 1.21 log_level=verbose control=true tunnel_forward=true"
if self.displayid is not None:
- cmdline += f' display_id={self.displayid}'
+ cmdline += f" display_id={self.displayid}"
self.__server_stream: Socket = self.client.stream_shell(cmdline)
# Wait for server to start
response = self.__server_stream.recv(100)
logger.debug(response)
- if b'[server]' not in response:
+ if b"[server]" not in response:
raise ConnectionError(
- 'Failed to start scrcpy-server: ' + response.decode('utf-8', 'ignore'))
+ "Failed to start scrcpy-server: " + response.decode("utf-8", "ignore")
+ )
def __deploy_server(self) -> None:
"""
Deploy server to android device
"""
- server_file_path = __rootdir__ / 'vendor' / \
- 'scrcpy-server-novideo' / 'scrcpy-server-novideo.jar'
+ server_file_path = (
+ __rootdir__
+ / "vendor"
+ / "scrcpy-server-novideo"
+ / "scrcpy-server-novideo.jar"
+ )
server_buf = server_file_path.read_bytes()
self.client.push(SCR_PATH, server_buf)
self.__start_server()
@@ -108,26 +113,26 @@ def __init_server_connection(self) -> None:
This method will set: video_socket, control_socket, resolution variables
"""
try:
- self.__video_socket = self.client.stream('localabstract:scrcpy')
+ self.__video_socket = self.client.stream("localabstract:scrcpy")
except socket.timeout:
- raise ConnectionError('Failed to connect scrcpy-server')
+ raise ConnectionError("Failed to connect scrcpy-server")
dummy_byte = self.__video_socket.recv(1)
- if not len(dummy_byte) or dummy_byte != b'\x00':
- raise ConnectionError('Did not receive Dummy Byte!')
+ if not len(dummy_byte) or dummy_byte != b"\x00":
+ raise ConnectionError("Did not receive Dummy Byte!")
try:
- self.control_socket = self.client.stream('localabstract:scrcpy')
+ self.control_socket = self.client.stream("localabstract:scrcpy")
except socket.timeout:
- raise ConnectionError('Failed to connect scrcpy-server')
+ raise ConnectionError("Failed to connect scrcpy-server")
- self.device_name = self.__video_socket.recv(64).decode('utf-8')
- self.device_name = self.device_name.rstrip('\x00')
+ self.device_name = self.__video_socket.recv(64).decode("utf-8")
+ self.device_name = self.device_name.rstrip("\x00")
if not len(self.device_name):
- raise ConnectionError('Did not receive Device Name!')
+ raise ConnectionError("Did not receive Device Name!")
res = self.__video_socket.recv(4)
- self.resolution = struct.unpack('>HH', res)
+ self.resolution = struct.unpack(">HH", res)
# self.__video_socket.setblocking(False)
def start(self) -> None:
@@ -143,13 +148,13 @@ def start(self) -> None:
break
except ConnectionError:
logger.debug(traceback.format_exc())
- logger.warning('Failed to connect scrcpy-server.')
+ logger.warning("Failed to connect scrcpy-server.")
self.stop()
- logger.warning('Try again in 10 seconds...')
+ logger.warning("Try again in 10 seconds...")
time.sleep(10)
try_count += 1
else:
- raise RuntimeError('Failed to connect scrcpy-server.')
+ raise RuntimeError("Failed to connect scrcpy-server.")
def stop(self) -> None:
"""
@@ -166,7 +171,7 @@ def stop(self) -> None:
self.__video_socket = None
def check_adb_alive(self) -> bool:
- """ check if adb server alive """
+ """check if adb server alive"""
return self.client.check_server_alive()
def stable(f):
@@ -184,7 +189,8 @@ def inner(self: Client, *args, **kwargs):
self.start()
try_count += 1
else:
- raise RuntimeError('Failed to start scrcpy-server.')
+ raise RuntimeError("Failed to start scrcpy-server.")
+
return inner
@stable
@@ -192,7 +198,17 @@ def tap(self, x: int, y: int) -> None:
self.control.tap(x, y)
@stable
- def swipe(self, x0, y0, x1, y1, move_duraion: float = 1, hold_before_release: float = 0, fall: bool = True, lift: bool = True):
+ def swipe(
+ self,
+ x0,
+ y0,
+ x1,
+ y1,
+ move_duraion: float = 1,
+ hold_before_release: float = 0,
+ fall: bool = True,
+ lift: bool = True,
+ ):
frame_time = 1 / 60
start_time = time.perf_counter()
@@ -208,8 +224,11 @@ def swipe(self, x0, y0, x1, y1, move_duraion: float = 1, hold_before_release: fl
break
time_progress = (t0 - start_time) / move_duraion
path_progress = time_progress
- self.control.touch(int(x0 + (x1 - x0) * path_progress),
- int(y0 + (y1 - y0) * path_progress), const.ACTION_MOVE)
+ self.control.touch(
+ int(x0 + (x1 - x0) * path_progress),
+ int(y0 + (y1 - y0) * path_progress),
+ const.ACTION_MOVE,
+ )
t1 = time.perf_counter()
step_time = t1 - t0
if step_time < frame_time:
diff --git a/arknights_mower/utils/device/utils.py b/arknights_mower/utils/device/utils.py
index 2a203b80e..a913d6740 100644
--- a/arknights_mower/utils/device/utils.py
+++ b/arknights_mower/utils/device/utils.py
@@ -1,24 +1,20 @@
-from __future__ import annotations
-
-import http
-import socket
import tempfile
import requests
-from ... import __system__
from ..log import logger
def download_file(target_url: str) -> str:
- """ download file to temp path, and return its file path for further usage """
- logger.debug(f'downloading: {target_url}')
+ """download file to temp path, and return its file path for further usage"""
+ logger.debug(f"downloading: {target_url}")
resp = requests.get(target_url, verify=False)
- with tempfile.NamedTemporaryFile('wb+', delete=False) as f:
+ with tempfile.NamedTemporaryFile("wb+", delete=False) as f:
file_name = f.name
f.write(resp.content)
return file_name
+
# def is_port_using(host: str, port: int) -> bool:
# """ if port is using by others, return True. else return False """
# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
diff --git a/arknights_mower/utils/digit_reader.py b/arknights_mower/utils/digit_reader.py
new file mode 100644
index 000000000..aeedafea4
--- /dev/null
+++ b/arknights_mower/utils/digit_reader.py
@@ -0,0 +1,117 @@
+import os
+from pathlib import Path
+
+import cv2
+import numpy as np
+
+from .image import loadres
+
+
+class DigitReader:
+ def __init__(self, template_dir=None):
+ if not template_dir:
+ template_dir = Path(os.path.dirname(os.path.abspath(__file__))) / Path(
+ "templates"
+ )
+ if not isinstance(template_dir, Path):
+ template_dir = Path(template_dir)
+ self.time_template = []
+ self.drone_template = []
+ for i in range(10):
+ self.time_template.append(loadres(f"orders_time/{i}", True))
+ self.drone_template.append(loadres(f"drone_count/{i}", True))
+
+ def get_drone(self, img_grey, h=1080, w=1920):
+ drone_part = img_grey[
+ h * 32 // 1080 : h * 76 // 1080, w * 1144 // 1920 : w * 1225 // 1920
+ ]
+ drone_part = cv2.resize(drone_part, (81, 44), interpolation=cv2.INTER_AREA)
+ result = {}
+ for j in range(10):
+ res = cv2.matchTemplate(
+ drone_part,
+ self.drone_template[j],
+ cv2.TM_CCORR_NORMED,
+ )
+ threshold = 0.95
+ loc = np.where(res >= threshold)
+ for i in range(len(loc[0])):
+ offset = loc[1][i]
+ accept = True
+ for o in result:
+ if abs(o - offset) < 5:
+ accept = False
+ break
+ if accept:
+ result[loc[1][i]] = j
+ ch = [str(result[k]) for k in sorted(result)]
+ return int("".join(ch))
+
+ def get_time(self, img_grey, h, w):
+ digit_part = img_grey[h * 510 // 1080 : h * 543 // 1080, w * 499 // 1920 : w]
+ digit_part = cv2.resize(digit_part, (1421, 33), interpolation=cv2.INTER_AREA)
+ result = {}
+ for j in range(10):
+ res = cv2.matchTemplate(
+ digit_part,
+ self.time_template[j],
+ cv2.TM_CCOEFF_NORMED,
+ )
+ threshold = 0.85
+ loc = np.where(res >= threshold)
+ for i in range(len(loc[0])):
+ x = loc[1][i]
+ accept = True
+ for o in result:
+ if abs(o - x) < 5:
+ accept = False
+ break
+ if accept:
+ if len(result) == 0:
+ digit_part = digit_part[:, loc[1][i] - 5 : loc[1][i] + 116]
+ offset = loc[1][0] - 5
+ for m in range(len(loc[1])):
+ loc[1][m] -= offset
+ result[loc[1][i]] = j
+ ch = [str(result[k]) for k in sorted(result)]
+ return f"{ch[0]}{ch[1]}:{ch[2]}{ch[3]}:{ch[4]}{ch[5]}"
+
+ def 识别制造加速总剩余时间(self, img_grey, h, w):
+ 时间部分 = img_grey[
+ h * 665 // 1080 : h * 709 // 1080, w * 750 // 1920 : w * 960 // 1920
+ ]
+ 时间部分 = cv2.resize(
+ 时间部分, (210 * 58 // 71, 44 * 58 // 71), interpolation=cv2.INTER_AREA
+ )
+ result = {}
+ for j in range(10):
+ res = cv2.matchTemplate(
+ 时间部分,
+ self.drone_template[j],
+ cv2.TM_CCOEFF_NORMED,
+ )
+ threshold = 0.85
+ loc = np.where(res >= threshold)
+ for i in range(len(loc[0])):
+ offset = loc[1][i]
+ accept = True
+ for o in result:
+ if abs(o - offset) < 5:
+ accept = False
+ break
+ if accept:
+ result[loc[1][i]] = j
+ ch = [str(result[k]) for k in sorted(result)]
+ print(ch)
+ if len(ch) == 6:
+ return (
+ int(f"{ch[0]}{ch[1]}"),
+ int(f"{ch[2]}{ch[3]}"),
+ int(f"{ch[4]}{ch[5]}"),
+ )
+ else:
+ return (
+ int(f"{ch[0]}{ch[1]}{ch[2]}"),
+ int(f"{ch[3]}{ch[4]}"),
+ int(f"{ch[5]}{ch[6]}"),
+ )
diff --git a/arknights_mower/utils/email.py b/arknights_mower/utils/email.py
new file mode 100644
index 000000000..6cd90a5cd
--- /dev/null
+++ b/arknights_mower/utils/email.py
@@ -0,0 +1,103 @@
+import smtplib
+from email.mime.image import MIMEImage
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+from threading import Thread
+from time import sleep
+from typing import Literal, Optional
+
+from jinja2 import Environment, FileSystemLoader, select_autoescape
+
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.image import img2bytes
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+template_dir = get_path("@internal/arknights_mower/templates")
+env = Environment(loader=FileSystemLoader(template_dir), autoescape=select_autoescape())
+
+task_template = env.get_template("task.html")
+maa_template = env.get_template("maa.html")
+recruit_template = env.get_template("recruit_template.html")
+recruit_rarity = env.get_template("recruit_rarity.html")
+report_template = env.get_template("report_template.html")
+version_template = env.get_template("version.html")
+
+
+class Email:
+ def __init__(self, body, subject, attach_image):
+ conf = config.conf
+ msg = MIMEMultipart()
+ msg.attach(MIMEText(body, "html"))
+ msg["Subject"] = subject
+ msg["From"] = conf.account
+ msg["To"] = ", ".join(conf.recipient)
+
+ if attach_image is not None:
+ attachment = img2bytes(attach_image)
+ image_content = MIMEImage(attachment.tobytes())
+ image_content.add_header(
+ "Content-Disposition", "attachment", filename="image.jpg"
+ )
+ msg.attach(image_content)
+ self.msg = msg
+
+ if conf.custom_smtp_server.enable:
+ self.smtp_server = conf.custom_smtp_server.server
+ self.port = conf.custom_smtp_server.ssl_port
+ self.encryption = conf.custom_smtp_server.encryption
+ else:
+ self.smtp_server = "smtp.qq.com"
+ self.port = 465
+ self.encryption = "tls"
+
+ def send(self):
+ if self.encryption == "starttls":
+ s = smtplib.SMTP(self.smtp_server, self.port, timeout=10)
+ s.starttls()
+ else:
+ s = smtplib.SMTP_SSL(self.smtp_server, self.port, timeout=10)
+ conf = config.conf
+ s.login(conf.account, conf.pass_code)
+ recipient = conf.recipient or [conf.account]
+ s.send_message(self.msg, conf.account, recipient)
+ s.quit()
+
+
+def send_message(
+ body="",
+ subject="",
+ level: Literal["INFO", "WARNING", "ERROR"] = "INFO",
+ attach_image: Optional[tp.Image] = None,
+):
+ """异步发送邮件
+
+ Args:
+ body: 邮件内容
+ subject: 邮件标题
+ level: 通知等级
+ attach_image: 图片附件
+ """
+ conf = config.conf
+ if not conf.mail_enable:
+ return
+ if conf.notification_level == "WARNING" and level == "INFO":
+ return
+ if conf.notification_level == "ERROR" and level != "ERROR":
+ return
+ if subject == "":
+ subject = body.split("\n")[0].strip()
+ subject = conf.mail_subject + subject
+ email = Email(body, subject, attach_image)
+
+ def send_message_sync(email):
+ for i in range(3):
+ try:
+ email.send()
+ break
+ except Exception as e:
+ logger.exception("邮件发送失败:" + str(e))
+ sleep(2**i)
+
+ Thread(target=send_message_sync, args=(email,)).start()
diff --git a/arknights_mower/utils/git_rev.py b/arknights_mower/utils/git_rev.py
new file mode 100644
index 000000000..c30d6658d
--- /dev/null
+++ b/arknights_mower/utils/git_rev.py
@@ -0,0 +1,161 @@
+"""
+https://gist.github.com/pkienzle/5e13ec07077d32985fa48ebe43486832
+
+Get commit id from the git repo.
+
+Drop the file rev.py into directory PACKAGE_PATH of your application. From
+within that package you can then do::
+
+ from . import rev
+
+ rev.print_revision() # print the repo version
+ commit = rev.revision_info() # return commit id
+
+On "pip install" the repo root directory will not be available. In this
+case the code looks for PACKAGE_NAME/git_revision, which you need to
+install into site-pacakges along with your other sources.
+
+The simplest way to create PACKAGE_NAME/git_revision is to run rev
+from setup.py::
+
+ import sys
+ import os
+
+ # Create the resource file git_revision.
+ if os.system(f'"{sys.executable}" PACKAGE_NAME/rev.py') != 0:
+ print("setup.py failed to build PACKAGE_NAME/git_revision", file=sys.stderr)
+ sys.exit(1)
+
+ ...
+
+ # Include git revision in the package data, eitherj by adding
+ # "include PACKAGE_NAME/git_revision" to MANIFEST.in, or by
+ # adding the following to setup.py:
+ #package_data = {"PACKAGE_NAME": ["git_revision"]}
+ setup(
+ ...
+ #package_data=package_data,
+ include_package_data=True,
+ ...
+ )
+
+Add the following to .gitignore, substituting your package name::
+
+ /PACKAGE_NAME/git_revision
+
+"""
+
+from pathlib import Path
+from warnings import warn
+
+
+def repo_path():
+ """Return the root of the project git repo or None if not in a repo."""
+ base = Path(__file__).absolute()
+ for path in base.parents:
+ if (path / ".git").exists():
+ return path
+ return None
+
+
+def print_revision():
+ """Print the git revision"""
+ revision = revision_info()
+ print("git revision", revision)
+
+
+def store_revision():
+ """
+ Call from setup.py to save the git revision to the distribution.
+
+ See :mod:`rev` for details.
+ """
+ commit = git_rev(repo_path())
+ path = Path(__file__).absolute().parent / RESOURCE_NAME
+ with path.open("w") as fd:
+ fd.write(commit + "\n")
+
+
+RESOURCE_NAME = "git_revision"
+_REVISION_INFO = None # cached value of git revision
+
+
+def revision_info():
+ """
+ Get the git hash and mtime of the repository, or the installed files.
+ """
+ # TODO: test with "pip install -e ." for developer mode
+ global _REVISION_INFO
+
+ if _REVISION_INFO is None:
+ _REVISION_INFO = git_rev(repo_path())
+
+ if _REVISION_INFO is None:
+ try:
+ from importlib import resources
+ except ImportError: # CRUFT: pre-3.7 requires importlib_resources
+ import importlib_resources as resources
+ try:
+ # Parse __name__ as "package.lib.rev" into "package.lib"
+ package = __name__.rsplit(".", 1)[0]
+ revdata = resources.read_text(package, RESOURCE_NAME)
+ commit = revdata.strip()
+ _REVISION_INFO = commit
+ except Exception:
+ _REVISION_INFO = "unknown"
+
+ return _REVISION_INFO
+
+
+def git_rev(repo):
+ """
+ Get the git revision for the repo in the path *repo*.
+
+ Returns the commit id of the current head.
+
+ Note: this function parses the files in the git repository directory
+ without using the git application. It may break if the structure of
+ the git repository changes. It only reads files, so it should not do
+ any damage to the repository in the process.
+ """
+ # Based on stackoverflow am9417
+ # https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script/59950703#59950703
+ if repo is None:
+ return None
+
+ git_root = Path(repo) / ".git"
+ git_head = git_root / "HEAD"
+ if not git_head.exists():
+ return None
+
+ # Read .git/HEAD file
+ with git_head.open("r") as fd:
+ head_ref = fd.read()
+
+ # Find head file .git/HEAD (e.g. ref: ref/heads/master => .git/ref/heads/master)
+ if not head_ref.startswith("ref: "):
+ return head_ref
+ head_ref = head_ref[5:].strip()
+
+ # Read commit id from head file
+ head_path = git_root.joinpath(*head_ref.split("/"))
+ if not head_path.exists():
+ warn(f"path {head_path} referenced from {git_head} does not exist")
+ return None
+
+ with head_path.open("r") as fd:
+ commit = fd.read().strip()
+
+ return commit
+
+
+def main():
+ """
+ When run as a python script create git_revision in the current directory.
+ """
+ print_revision()
+ store_revision()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/arknights_mower/utils/graph.py b/arknights_mower/utils/graph.py
new file mode 100644
index 000000000..7a243113f
--- /dev/null
+++ b/arknights_mower/utils/graph.py
@@ -0,0 +1,486 @@
+import functools
+
+import networkx as nx
+
+from arknights_mower.utils import config
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.device.adb_client.session import Session
+from arknights_mower.utils.device.scrcpy import Scrcpy
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.scene import Scene, SceneComment
+from arknights_mower.utils.simulator import restart_simulator
+from arknights_mower.utils.solver import BaseSolver
+
+DG = nx.DiGraph()
+
+
+def edge(v_from: int, v_to: int, interval: int = 1):
+ def decorator(func):
+ DG.add_edge(v_from, v_to, weight=interval, transition=func)
+
+ @functools.wraps(func)
+ def wrapper(*args, **kw):
+ return func(*args, **kw)
+
+ return wrapper
+
+ return decorator
+
+
+# 首页
+
+
+@edge(Scene.INFRA_MAIN, Scene.INDEX)
+@edge(Scene.MISSION_DAILY, Scene.INDEX)
+@edge(Scene.MISSION_WEEKLY, Scene.INDEX)
+@edge(Scene.MISSION_TRAINEE, Scene.INDEX)
+@edge(Scene.BUSINESS_CARD, Scene.INDEX)
+@edge(Scene.FRIEND_LIST, Scene.INDEX)
+@edge(Scene.RECRUIT_MAIN, Scene.INDEX)
+@edge(Scene.SHOP_OTHERS, Scene.INDEX)
+@edge(Scene.SHOP_CREDIT, Scene.INDEX)
+@edge(Scene.TERMINAL_MAIN, Scene.INDEX)
+@edge(Scene.TERMINAL_MAIN_THEME, Scene.INDEX)
+@edge(Scene.TERMINAL_EPISODE, Scene.INDEX)
+@edge(Scene.TERMINAL_BIOGRAPHY, Scene.INDEX)
+@edge(Scene.TERMINAL_COLLECTION, Scene.INDEX)
+@edge(Scene.TERMINAL_REGULAR, Scene.INDEX)
+@edge(Scene.TERMINAL_LONGTERM, Scene.INDEX)
+@edge(Scene.TERMINAL_REGULAR, Scene.INDEX)
+@edge(Scene.DEPOT, Scene.INDEX)
+@edge(Scene.HEADHUNTING, Scene.INDEX)
+@edge(Scene.MAIL, Scene.INDEX)
+def back_to_index(solver: BaseSolver):
+ solver.back()
+
+
+@edge(Scene.LEAVE_INFRASTRUCTURE, Scene.INDEX)
+def leave_infrastructure(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=1)
+
+
+@edge(Scene.DOWNLOAD_VOICE_RESOURCES, Scene.INDEX)
+def dont_download_voice(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=0)
+
+
+@edge(Scene.LOGIN_QUICKLY, Scene.INDEX)
+def login_quickly(solver: BaseSolver):
+ solver.tap_element("login_awake")
+
+
+@edge(Scene.LOGIN_CAPTCHA, Scene.INDEX)
+def login_captcha(solver: BaseSolver):
+ solver.solve_captcha()
+ solver.sleep(5)
+
+
+@edge(Scene.LOGIN_BILIBILI, Scene.INDEX)
+@edge(Scene.LOGIN_BILIBILI_PRIVACY, Scene.INDEX)
+def login_bilibili(solver: BaseSolver):
+ solver.bilibili()
+
+
+@edge(Scene.EXIT_GAME, Scene.INDEX)
+def exit_cancel(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=0)
+
+
+@edge(Scene.MATERIEL, Scene.INDEX)
+def materiel(solver: BaseSolver):
+ solver.tap((960, 960))
+
+
+@edge(Scene.ANNOUNCEMENT, Scene.INDEX)
+def announcement(solver: BaseSolver):
+ solver.tap(solver.recog.check_announcement())
+
+
+@edge(Scene.AGREEMENT_UPDATE, Scene.INDEX)
+def agreement(solver: BaseSolver):
+ if pos := solver.find("read_and_agree"):
+ solver.tap(pos)
+ else:
+ solver.tap((791, 728))
+ solver.tap((959, 828))
+
+
+@edge(Scene.INDEX, Scene.INFRA_MAIN)
+def index_to_infra(solver: BaseSolver):
+ solver.tap_index_element("infrastructure")
+
+
+@edge(Scene.INDEX, Scene.BUSINESS_CARD)
+def index_to_friend(solver: BaseSolver):
+ solver.tap_index_element("friend")
+
+
+@edge(Scene.INDEX, Scene.MISSION_DAILY)
+def index_to_mission(solver: BaseSolver):
+ solver.tap_index_element("mission")
+
+
+@edge(Scene.INDEX, Scene.RECRUIT_MAIN)
+def index_to_recruit(solver: BaseSolver):
+ solver.tap_index_element("recruit")
+
+
+@edge(Scene.INDEX, Scene.SHOP_OTHERS)
+def index_to_shop(solver: BaseSolver):
+ solver.tap_index_element("shop")
+
+
+@edge(Scene.INDEX, Scene.TERMINAL_MAIN)
+def index_to_terminal(solver: BaseSolver):
+ solver.tap_index_element("terminal")
+
+
+@edge(Scene.INDEX, Scene.DEPOT)
+def index_to_depot(solver: BaseSolver):
+ solver.tap_index_element("warehouse")
+
+
+@edge(Scene.INDEX, Scene.MAIL)
+def index_to_mail(solver: BaseSolver):
+ solver.tap_index_element("mail")
+
+
+@edge(Scene.INDEX, Scene.HEADHUNTING)
+def index_to_headhunting(solver: BaseSolver):
+ solver.tap_index_element("headhunting")
+
+
+# 导航栏
+
+
+@edge(Scene.INFRA_MAIN, Scene.NAVIGATION_BAR)
+@edge(Scene.RECRUIT_MAIN, Scene.NAVIGATION_BAR)
+@edge(Scene.RECRUIT_TAGS, Scene.NAVIGATION_BAR)
+@edge(Scene.MISSION_DAILY, Scene.NAVIGATION_BAR)
+@edge(Scene.MISSION_WEEKLY, Scene.NAVIGATION_BAR)
+@edge(Scene.MISSION_TRAINEE, Scene.NAVIGATION_BAR)
+@edge(Scene.BUSINESS_CARD, Scene.NAVIGATION_BAR)
+@edge(Scene.FRIEND_LIST, Scene.NAVIGATION_BAR)
+@edge(Scene.SHOP_OTHERS, Scene.NAVIGATION_BAR)
+@edge(Scene.SHOP_CREDIT, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_MAIN, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_MAIN_THEME, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_EPISODE, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_BIOGRAPHY, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_COLLECTION, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_REGULAR, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_LONGTERM, Scene.NAVIGATION_BAR)
+@edge(Scene.TERMINAL_PERIODIC, Scene.NAVIGATION_BAR)
+@edge(Scene.OPERATOR_CHOOSE_LEVEL, Scene.NAVIGATION_BAR)
+@edge(Scene.OPERATOR_BEFORE, Scene.NAVIGATION_BAR)
+@edge(Scene.OPERATOR_SELECT, Scene.NAVIGATION_BAR)
+@edge(Scene.OPERATOR_SUPPORT, Scene.NAVIGATION_BAR)
+@edge(Scene.INFRA_TODOLIST, Scene.NAVIGATION_BAR)
+@edge(Scene.INFRA_CONFIDENTIAL, Scene.NAVIGATION_BAR)
+@edge(Scene.INFRA_ARRANGE, Scene.NAVIGATION_BAR)
+@edge(Scene.INFRA_DETAILS, Scene.NAVIGATION_BAR)
+@edge(Scene.CTRLCENTER_ASSISTANT, Scene.NAVIGATION_BAR)
+@edge(Scene.CLUE_DAILY, Scene.NAVIGATION_BAR)
+@edge(Scene.CLUE_RECEIVE, Scene.NAVIGATION_BAR)
+@edge(Scene.CLUE_PLACE, Scene.NAVIGATION_BAR)
+@edge(Scene.ORDER_LIST, Scene.NAVIGATION_BAR)
+@edge(Scene.FACTORY_ROOMS, Scene.NAVIGATION_BAR)
+@edge(Scene.OPERATOR_ELIMINATE, Scene.NAVIGATION_BAR)
+@edge(Scene.DEPOT, Scene.NAVIGATION_BAR)
+@edge(Scene.FRIEND_VISITING, Scene.NAVIGATION_BAR)
+@edge(Scene.HEADHUNTING, Scene.NAVIGATION_BAR)
+def index_nav(solver: BaseSolver):
+ solver.tap_element("nav_button")
+
+
+# 不加从导航栏到基建首页的边,防止在基建内循环
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.MISSION_DAILY)
+def nav_mission(solver: BaseSolver):
+ solver.tap_nav_element("mission")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.INDEX)
+def nav_index(solver: BaseSolver):
+ solver.tap_nav_element("index")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.TERMINAL_MAIN)
+def nav_terminal(solver: BaseSolver):
+ solver.tap_nav_element("terminal")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.RECRUIT_MAIN)
+def nav_recruit(solver: BaseSolver):
+ solver.tap_nav_element("recruit")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.SHOP_OTHERS)
+def nav_shop(solver: BaseSolver):
+ solver.tap_nav_element("shop")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.HEADHUNTING)
+def nav_headhunting(solver: BaseSolver):
+ solver.tap_nav_element("headhunting")
+
+
+@edge(Scene.NAVIGATION_BAR, Scene.BUSINESS_CARD)
+def nav_friend(solver: BaseSolver):
+ solver.tap_nav_element("friend")
+
+
+# 任务
+
+
+@edge(Scene.MISSION_DAILY, Scene.MISSION_WEEKLY)
+@edge(Scene.MISSION_TRAINEE, Scene.MISSION_WEEKLY)
+def mission_to_weekly(solver: BaseSolver):
+ solver.tap_element("mission_weekly")
+
+
+@edge(Scene.MISSION_TRAINEE, Scene.MISSION_DAILY)
+def mission_trainee_to_daily(solver: BaseSolver):
+ solver.tap_element("mission_daily")
+
+
+# 商店
+
+
+@edge(Scene.SHOP_OTHERS, Scene.SHOP_CREDIT)
+def shop_to_credit(solver: BaseSolver):
+ solver.tap_element("shop_credit_2")
+
+
+@edge(Scene.SHOP_CREDIT_CONFIRM, Scene.SHOP_CREDIT)
+def shop_confirm(solver: BaseSolver):
+ solver.back()
+
+
+# 好友
+
+
+@edge(Scene.BUSINESS_CARD, Scene.FRIEND_LIST)
+def friend_list(solver: BaseSolver):
+ solver.tap((194, 333))
+
+
+@edge(Scene.FRIEND_LIST, Scene.BUSINESS_CARD)
+def business_card(solver: BaseSolver):
+ solver.tap((188, 198))
+
+
+@edge(Scene.FRIEND_VISITING, Scene.BACK_TO_FRIEND_LIST)
+def friend_visiting_back(solver: BaseSolver):
+ solver.back()
+
+
+@edge(Scene.BACK_TO_FRIEND_LIST, Scene.BUSINESS_CARD)
+def back_to_friend_confirm(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=1)
+
+
+# 作战
+
+
+@edge(Scene.TERMINAL_MAIN, Scene.TERMINAL_MAIN_THEME)
+@edge(Scene.TERMINAL_COLLECTION, Scene.TERMINAL_MAIN_THEME)
+@edge(Scene.TERMINAL_REGULAR, Scene.TERMINAL_MAIN_THEME)
+@edge(Scene.TERMINAL_LONGTERM, Scene.TERMINAL_MAIN_THEME)
+@edge(Scene.TERMINAL_PERIODIC, Scene.TERMINAL_MAIN_THEME)
+def terminal_to_main_theme(solver: BaseSolver):
+ solver.tap_terminal_button("main_theme")
+
+
+@edge(Scene.TERMINAL_MAIN, Scene.TERMINAL_BIOGRAPHY)
+@edge(Scene.TERMINAL_COLLECTION, Scene.TERMINAL_BIOGRAPHY)
+@edge(Scene.TERMINAL_REGULAR, Scene.TERMINAL_BIOGRAPHY)
+@edge(Scene.TERMINAL_LONGTERM, Scene.TERMINAL_BIOGRAPHY)
+@edge(Scene.TERMINAL_PERIODIC, Scene.TERMINAL_BIOGRAPHY)
+def terminal_to_biography(solver: BaseSolver):
+ solver.tap_terminal_button("biography")
+
+
+@edge(Scene.OPERATOR_RECOVER_POTION, Scene.OPERATOR_BEFORE)
+@edge(Scene.OPERATOR_RECOVER_ORIGINITE, Scene.OPERATOR_BEFORE)
+@edge(Scene.OPERATOR_BEFORE, Scene.OPERATOR_CHOOSE_LEVEL)
+@edge(Scene.OPERATOR_CHOOSE_LEVEL, Scene.TERMINAL_MAIN_THEME)
+@edge(Scene.OPERATOR_CHOOSE_LEVEL, Scene.TERMINAL_BIOGRAPHY)
+@edge(Scene.OPERATOR_CHOOSE_LEVEL, Scene.TERMINAL_COLLECTION)
+@edge(Scene.OPERATOR_SUPPORT, Scene.OPERATOR_SELECT)
+@edge(Scene.OPERATOR_STRANGER_SUPPORT, Scene.OPERATOR_SUPPORT)
+@edge(Scene.OPERATOR_ELIMINATE_AGENCY, Scene.OPERATOR_ELIMINATE)
+def operation_back(solver: BaseSolver):
+ solver.back()
+
+
+@edge(Scene.OPERATOR_GIVEUP, Scene.OPERATOR_FAILED)
+def operation_give_up(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=1)
+
+
+@edge(Scene.OPERATOR_FINISH, Scene.OPERATOR_BEFORE)
+@edge(Scene.OPERATOR_FAILED, Scene.OPERATOR_BEFORE)
+def operation_finish(solver: BaseSolver):
+ solver.tap((310, 330))
+
+
+@edge(Scene.UPGRADE, Scene.OPERATOR_FINISH)
+def upgrade(solver: BaseSolver):
+ solver.tap((960, 540))
+
+
+# 基建
+
+
+@edge(Scene.INFRA_TODOLIST, Scene.INFRA_MAIN)
+def todo_complete(solver: BaseSolver):
+ solver.tap((1840, 140))
+
+
+@edge(Scene.INFRA_CONFIDENTIAL, Scene.INFRA_MAIN)
+@edge(Scene.INFRA_ARRANGE, Scene.INFRA_MAIN)
+@edge(Scene.INFRA_DETAILS, Scene.INFRA_MAIN)
+@edge(Scene.CTRLCENTER_ASSISTANT, Scene.INFRA_MAIN)
+@edge(Scene.RIIC_OPERATOR_SELECT, Scene.INFRA_DETAILS)
+@edge(Scene.CLUE_DAILY, Scene.INFRA_CONFIDENTIAL)
+@edge(Scene.CLUE_RECEIVE, Scene.INFRA_CONFIDENTIAL)
+@edge(Scene.CLUE_GIVE_AWAY, Scene.INFRA_CONFIDENTIAL)
+@edge(Scene.CLUE_SUMMARY, Scene.INFRA_CONFIDENTIAL)
+@edge(Scene.CLUE_PLACE, Scene.INFRA_CONFIDENTIAL)
+@edge(Scene.ORDER_LIST, Scene.INFRA_DETAILS)
+@edge(Scene.FACTORY_ROOMS, Scene.INFRA_DETAILS)
+@edge(Scene.DRONE_ACCELERATE, Scene.ORDER_LIST)
+def infra_back(solver: BaseSolver):
+ solver.back()
+
+
+@edge(Scene.INFRA_ARRANGE_CONFIRM, Scene.INFRA_DETAILS)
+def infra_arrange_confirm(solver: BaseSolver):
+ solver.tap((1452, 1029))
+
+
+@edge(Scene.INFRA_ARRANGE_ORDER, Scene.INFRA_DETAILS)
+def infra_arrange_order(solver: BaseSolver):
+ solver.tap_element("arrange_blue_yes", x_rate=0.66)
+
+
+@edge(Scene.RIIC_REPORT, Scene.CTRLCENTER_ASSISTANT)
+def riic_back(solver: BaseSolver):
+ solver.tap((30, 55))
+
+
+@edge(Scene.CTRLCENTER_ASSISTANT, Scene.RIIC_REPORT)
+def riic(solver: BaseSolver):
+ solver.tap_element("control_central_assistants")
+
+
+@edge(Scene.INFRA_MAIN, Scene.CTRLCENTER_ASSISTANT)
+def control_central(solver: BaseSolver):
+ solver.tap_element("control_central")
+
+
+# 公招
+
+
+@edge(Scene.RECRUIT_AGENT, Scene.RECRUIT_MAIN)
+def recruit_result(solver: BaseSolver):
+ solver.tap((960, 540))
+
+
+@edge(Scene.REFRESH_TAGS, Scene.RECRUIT_TAGS)
+def refresh_cancel(solver: BaseSolver):
+ solver.tap_element("double_confirm/main", x_rate=0)
+
+
+@edge(Scene.RECRUIT_TAGS, Scene.RECRUIT_MAIN)
+def recruit_back(solver: BaseSolver):
+ solver.back()
+
+
+@edge(Scene.SKIP, Scene.RECRUIT_AGENT)
+def skip(solver: BaseSolver):
+ solver.tap_element("skip")
+
+
+# 其它场景
+
+
+@edge(Scene.UNDEFINED, Scene.INDEX)
+def get_scene(solver: BaseSolver):
+ solver.scene()
+
+
+@edge(Scene.LOGIN_START, Scene.LOGIN_QUICKLY)
+def login_start(solver: BaseSolver):
+ solver.tap((665, 741))
+
+
+@edge(Scene.CONFIRM, Scene.LOGIN_START)
+def confirm(solver: BaseSolver):
+ solver.tap_element("confirm")
+
+
+@edge(Scene.NETWORK_CHECK, Scene.LOGIN_START)
+def network_check_cancel(solver: BaseSolver):
+ solver.tap_element("confirm")
+
+
+class SceneGraphSolver(BaseSolver):
+ def scene_graph_navigation(self, scene: int):
+ if scene not in DG.nodes:
+ logger.error(f"{SceneComment[scene]}不在场景图中")
+ return False
+
+ while (current := self.scene()) != scene:
+ if current in self.waiting_scene:
+ self.waiting_solver()
+ continue
+
+ if current not in DG.nodes:
+ logger.debug(f"{SceneComment[current]}不在场景图中")
+ self.sleep()
+
+ try:
+ sp = nx.shortest_path(DG, current, scene, weight="weight")
+ except Exception as e:
+ logger.exception(f"场景图路径计算异常:{e}")
+ restart_simulator()
+ self.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ self.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ self.device.control.scrcpy = Scrcpy(self.device.client)
+ return False
+
+ logger.debug(sp)
+
+ next_scene = sp[1]
+ transition = DG.edges[current, next_scene]["transition"]
+
+ try:
+ transition(self)
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(f"场景转移异常:{e}")
+ restart_simulator()
+ self.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if config.conf.droidcast.enable:
+ self.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ self.device.control.scrcpy = Scrcpy(self.device.client)
+ self.check_current_focus()
+ return False
+ return True
+
+ def back_to_index(self):
+ logger.info("场景图导航:back_to_index")
+ self.scene_graph_navigation(Scene.INDEX)
+
+ def back_to_infrastructure(self):
+ logger.info("场景图导航:back_to_infrastructure")
+ self.scene_graph_navigation(Scene.INFRA_MAIN)
diff --git a/arknights_mower/utils/hot_update.py b/arknights_mower/utils/hot_update.py
new file mode 100644
index 000000000..add27a4bd
--- /dev/null
+++ b/arknights_mower/utils/hot_update.py
@@ -0,0 +1,89 @@
+import os
+import sys
+from datetime import datetime, timedelta
+from importlib import reload
+from io import BytesIO
+from shutil import rmtree
+from time import mktime
+from zipfile import ZipFile
+
+import requests
+from htmllistparse import fetch_listing
+
+from arknights_mower.utils.image import loadimg
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+extract_path = get_path("@install/tmp/hot_update")
+mirror = "https://mower.zhaozuohong.vip"
+sign_in = None
+navigation = None
+
+last_listing = None, []
+last_update = None
+
+
+def load_module(download_update):
+ global sign_in
+ global navigation
+ if "sign_in" in sys.modules and "navigation" in sys.modules:
+ if download_update:
+ loadimg.cache_clear()
+ reload(sign_in)
+ reload(navigation)
+ else:
+ if extract_path not in sys.path:
+ sys.path.append(str(extract_path))
+ import navigation
+ import sign_in
+
+
+def get_listing():
+ global last_listing
+ last_time, listing = last_listing
+ if last_time and datetime.now() - last_time < timedelta(minutes=10):
+ return listing
+ cwd, listing = fetch_listing(mirror)
+ last_listing = datetime.now(), listing
+ return listing
+
+
+def update():
+ global last_update
+
+ if last_update and datetime.now() - last_update < timedelta(minutes=30):
+ logger.info("跳过热更新检查")
+ load_module(False)
+ return
+
+ logger.info("检查热更新资源")
+ listing = get_listing()
+ filename = "hot_update.zip"
+ entry = next(i for i in listing if i.name == filename)
+ remote_time = datetime.fromtimestamp(mktime(entry.modified))
+ download_update = True
+ if extract_path.exists():
+ local_time = datetime.fromtimestamp(os.path.getctime(extract_path))
+ if local_time > remote_time:
+ download_update = False
+ else:
+ rmtree(extract_path)
+ if download_update:
+ logger.info("开始下载热更新资源")
+ retry_times = 3
+ for i in range(retry_times):
+ try:
+ r = requests.get(f"{mirror}/{filename}")
+ ZipFile(BytesIO(r.content)).extractall(extract_path)
+ break
+ except Exception as e:
+ logger.exception(f"热更新出错:{e}")
+ if i >= retry_times:
+ logger.error("热更新失败!")
+ return
+ logger.info("热更新成功")
+ else:
+ logger.info("本地资源已是最新")
+
+ last_update = datetime.now()
+ load_module(download_update)
diff --git a/arknights_mower/utils/image.py b/arknights_mower/utils/image.py
index c47946764..8a7d8a09f 100644
--- a/arknights_mower/utils/image.py
+++ b/arknights_mower/utils/image.py
@@ -1,14 +1,17 @@
+from functools import lru_cache
from typing import Union
import cv2
import numpy as np
-from . import typealias as tp
-from .log import logger, save_screenshot
+from arknights_mower import __rootdir__
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.log import logger, save_screenshot
+from arknights_mower.utils.path import get_path
def bytes2img(data: bytes, gray: bool = False) -> Union[tp.Image, tp.GrayImage]:
- """ bytes -> image """
+ """bytes -> image"""
if gray:
return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_GRAYSCALE)
else:
@@ -18,22 +21,39 @@ def bytes2img(data: bytes, gray: bool = False) -> Union[tp.Image, tp.GrayImage]:
)
-def img2bytes(img) -> bytes:
- """ bytes -> image """
- return cv2.imencode('.png', img)[1]
+def img2bytes(img: tp.Image) -> bytes:
+ """image -> bytes"""
+ return cv2.imencode(
+ ".jpg",
+ cv2.cvtColor(img, cv2.COLOR_RGB2BGR),
+ [int(cv2.IMWRITE_JPEG_QUALITY), 75],
+ )[1]
+def loadres(res: tp.Res, gray: bool = False) -> Union[tp.Image, tp.GrayImage]:
+ if res.startswith("@hot"):
+ res_name = res.replace("@hot", "@install/tmp/hot_update", 1)
+ else:
+ res_name = f"{__rootdir__}/resources/{res}"
+ if not res.endswith(".jpg"):
+ res_name += ".png"
+ filename = get_path(res_name, "")
+ return loadimg(filename, gray)
+
+
+@lru_cache(maxsize=128)
def loadimg(filename: str, gray: bool = False) -> Union[tp.Image, tp.GrayImage]:
- """ load image from file """
+ """load image from file"""
logger.debug(filename)
+ img_data = np.fromfile(filename, dtype=np.uint8)
if gray:
- return cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
+ return cv2.imdecode(img_data, cv2.IMREAD_GRAYSCALE)
else:
- return cv2.cvtColor(cv2.imread(filename, cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB)
+ return cv2.cvtColor(cv2.imdecode(img_data, cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB)
def thres2(img: tp.GrayImage, thresh: int) -> tp.GrayImage:
- """ binarization of images """
+ """binarization of images"""
_, ret = cv2.threshold(img, thresh, 255, cv2.THRESH_BINARY)
return ret
@@ -62,24 +82,48 @@ def thres2(img: tp.GrayImage, thresh: int) -> tp.GrayImage:
def rgb2gray(img: tp.Image) -> tp.GrayImage:
- """ change image from rgb to gray """
+ """change image from rgb to gray"""
return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
def scope2slice(scope: tp.Scope) -> tp.Slice:
- """ ((x0, y0), (x1, y1)) -> ((y0, y1), (x0, x1)) """
+ """((x0, y0), (x1, y1)) -> ((y0, y1), (x0, x1))"""
if scope is None:
return slice(None), slice(None)
return slice(scope[0][1], scope[1][1]), slice(scope[0][0], scope[1][0])
def cropimg(img: tp.Image, scope: tp.Scope) -> tp.Image:
- """ crop image """
+ """crop image"""
return img[scope2slice(scope)]
-def saveimg(img, folder='failure'):
- save_screenshot(
- img2bytes(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)),
- subdir=f'{folder}/{img.shape[0]}x{img.shape[1]}',
- )
+def saveimg(img: tp.Image, folder):
+ del folder # 兼容2024.05旧版接口
+ save_screenshot(img2bytes(img))
+
+
+def cmatch(
+ img1: tp.Image, img2: tp.Image, thresh: int = 10, draw: bool = False
+) -> tp.Scope | None:
+ """比较平均色"""
+ h, w, _ = img1.shape
+ ca = cv2.mean(img1)[:3]
+ cb = cv2.mean(img2)[:3]
+ diff = np.array(ca).astype(int) - np.array(cb).astype(int)
+ diff = np.max(np.maximum(diff, 0)) - np.min(np.minimum(diff, 0))
+ logger.debug(f"{ca=} {cb=} {diff=}")
+
+ if draw:
+ board = np.zeros([h + 5, w * 2, 3], dtype=np.uint8)
+ board[:h, :w, :] = img1
+ board[h:, :w, :] = ca
+ board[:h, w:, :] = img2
+ board[h:, w:, :] = cb
+
+ from matplotlib import pyplot as plt
+
+ plt.imshow(board)
+ plt.show()
+
+ return diff <= thresh
diff --git a/arknights_mower/utils/log.py b/arknights_mower/utils/log.py
index 2ef280eb8..d59dfa1fc 100644
--- a/arknights_mower/utils/log.py
+++ b/arknights_mower/utils/log.py
@@ -1,16 +1,18 @@
import logging
-import os
+import shutil
import sys
-import threading
import time
+import traceback
from logging.handlers import RotatingFileHandler
from pathlib import Path
import colorlog
-from . import config
-BASIC_FORMAT = '%(asctime)s - %(levelname)s - %(relativepath)s:%(lineno)d - %(funcName)s - %(message)s'
-COLOR_FORMAT = '%(log_color)s%(asctime)s - %(levelname)s - %(relativepath)s:%(lineno)d - %(funcName)s - %(message)s'
+from arknights_mower.utils import config
+from arknights_mower.utils.path import get_path
+
+BASIC_FORMAT = "%(asctime)s %(relativepath)s:%(lineno)d %(levelname)s %(message)s"
+COLOR_FORMAT = f"%(log_color)s{BASIC_FORMAT}"
DATE_FORMAT = None
basic_formatter = logging.Formatter(BASIC_FORMAT, DATE_FORMAT)
color_formatter = colorlog.ColoredFormatter(COLOR_FORMAT, DATE_FORMAT)
@@ -18,113 +20,67 @@
class PackagePathFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
- pathname = record.pathname
- record.relativepath = None
- abs_sys_paths = map(os.path.abspath, sys.path)
- for path in sorted(abs_sys_paths, key=len, reverse=True): # longer paths first
- if not path.endswith(os.sep):
- path += os.sep
- if pathname.startswith(path):
- record.relativepath = os.path.relpath(pathname, path)
- break
+ relativepath = Path(record.pathname)
+ try:
+ relativepath = relativepath.relative_to(get_path("@install"))
+ except ValueError:
+ pass
+ record.relativepath = relativepath
return True
-class MaxFilter(object):
- def __init__(self, max_level: int) -> None:
- self.max_level = max_level
-
- def filter(self, record: logging.LogRecord) -> bool:
- if record.levelno <= self.max_level:
- return True
-
+filter = PackagePathFilter()
-class Handler(logging.StreamHandler):
- def __init__(self, pipe):
- logging.StreamHandler.__init__(self)
- self.pipe = pipe
- def emit(self, record):
- record = f'{record.message}'
- self.pipe.send(record)
+logger = logging.getLogger(__name__)
+logger.setLevel("DEBUG")
+
+dhlr = logging.StreamHandler(stream=sys.stdout)
+dhlr.setFormatter(color_formatter)
+dhlr.setLevel("DEBUG")
+dhlr.addFilter(filter)
+logger.addHandler(dhlr)
+
+folder = Path(get_path("@app/log"))
+folder.mkdir(exist_ok=True, parents=True)
+fhlr = RotatingFileHandler(
+ folder.joinpath("runtime.log"),
+ encoding="utf8",
+ maxBytes=10 * 1024 * 1024,
+ backupCount=20,
+)
+fhlr.setFormatter(basic_formatter)
+fhlr.setLevel("DEBUG")
+fhlr.addFilter(filter)
+logger.addHandler(fhlr)
-chlr = logging.StreamHandler(stream=sys.stdout)
-chlr.setFormatter(color_formatter)
-chlr.setLevel('INFO')
-chlr.addFilter(MaxFilter(logging.INFO))
-chlr.addFilter(PackagePathFilter())
+class Handler(logging.StreamHandler):
+ def emit(self, record: logging.LogRecord):
+ msg = f"{record.asctime} {record.levelname} {record.message}"
+ if record.exc_info:
+ msg += "\n" + "".join(traceback.format_exception(*record.exc_info))
+ config.log_queue.put(msg)
-ehlr = logging.StreamHandler(stream=sys.stderr)
-ehlr.setFormatter(color_formatter)
-ehlr.setLevel('WARNING')
-ehlr.addFilter(PackagePathFilter())
-logger = logging.getLogger(__name__)
-logger.setLevel('DEBUG')
-logger.addHandler(chlr)
-logger.addHandler(ehlr)
+whlr = Handler()
+whlr.setLevel(logging.INFO)
+logger.addHandler(whlr)
-def init_fhlr(pipe) -> None:
- """ initialize log file """
- if config.LOGFILE_PATH is None:
- return
- folder = Path(config.LOGFILE_PATH)
+def save_screenshot(img: bytes) -> None:
+ folder = get_path("@app/screenshot")
folder.mkdir(exist_ok=True, parents=True)
- fhlr = RotatingFileHandler(
- folder.joinpath('runtime.log'),
- encoding='utf8',
- maxBytes=10 * 1024 * 1024,
- backupCount=config.LOGFILE_AMOUNT,
- )
- fhlr.setFormatter(basic_formatter)
- fhlr.setLevel('DEBUG')
- fhlr.addFilter(PackagePathFilter())
- logger.addHandler(fhlr)
- if pipe is not None:
- wh = Handler(pipe)
- wh.setLevel(logging.INFO)
- logger.addHandler(wh)
-
-
-def set_debug_mode() -> None:
- """ set debud mode on """
- if config.DEBUG_MODE:
- logger.info(f'Start debug mode, log is stored in {config.LOGFILE_PATH}')
- init_fhlr()
-
-
-def save_screenshot(img: bytes, subdir: str = '') -> None:
- """ save screenshot """
- if config.SCREENSHOT_PATH is None:
- return
- folder = Path(config.SCREENSHOT_PATH).joinpath(subdir)
- folder.mkdir(exist_ok=True, parents=True)
- if subdir != '-1' and len(list(folder.iterdir())) > config.SCREENSHOT_MAXNUM:
- for x in list(folder.iterdir())[: -config.SCREENSHOT_MAXNUM]:
- logger.debug(f'remove screenshot: {x.name}')
- x.unlink()
- filename = time.strftime('%Y%m%d%H%M%S.png', time.localtime())
- with folder.joinpath(filename).open('wb') as f:
+ time_ns = time.time_ns()
+ start_time_ns = time_ns - config.conf.screenshot * 3600 * 10**9
+ for i in folder.iterdir():
+ if i.is_dir():
+ shutil.rmtree(i)
+ elif not i.stem.isnumeric():
+ i.unlink()
+ elif int(i.stem) < start_time_ns:
+ i.unlink()
+ filename = f"{time_ns}.jpg"
+ with folder.joinpath(filename).open("wb") as f:
f.write(img)
- logger.debug(f'save screenshot: {filename}')
-
-
-class log_sync(threading.Thread):
- """ recv output from subprocess """
-
- def __init__(self, process: str, pipe: int) -> None:
- self.process = process
- self.pipe = os.fdopen(pipe)
- super().__init__(daemon=True)
-
- def __del__(self) -> None:
- self.pipe.close()
-
- def run(self) -> None:
- while True:
- line = self.pipe.readline().strip()
- logger.debug(f'{self.process}: {line}')
-
-
+ logger.debug(f"save screenshot: {filename}")
diff --git a/arknights_mower/utils/logic_expression.py b/arknights_mower/utils/logic_expression.py
new file mode 100644
index 000000000..57128912f
--- /dev/null
+++ b/arknights_mower/utils/logic_expression.py
@@ -0,0 +1,25 @@
+from typing import Optional, Self
+
+
+class LogicExpression:
+ def __init__(
+ self,
+ left: Optional[str | Self] = None,
+ operator: Optional[str] = None,
+ right: Optional[str | Self] = None,
+ ):
+ self.operator = operator
+ self.left = left
+ self.right = right
+
+ def __str__(self):
+ return f"({(self.left)} {self.operator} {(self.right)})"
+
+
+def get_logic_exp(trigger: dict) -> LogicExpression:
+ for k in ["left", "operator", "right"]:
+ if k not in trigger:
+ trigger[k] = ""
+ if not isinstance(trigger[k], str):
+ trigger[k] = get_logic_exp(trigger[k])
+ return LogicExpression(trigger["left"], trigger["operator"], trigger["right"])
diff --git a/arknights_mower/utils/matcher.py b/arknights_mower/utils/matcher.py
index 0a5ffae5d..c60abc7e4 100644
--- a/arknights_mower/utils/matcher.py
+++ b/arknights_mower/utils/matcher.py
@@ -1,183 +1,255 @@
-from __future__ import annotations
-
+import lzma
import pickle
-import traceback
from typing import Optional, Tuple
import cv2
import numpy as np
-import sklearn
-from matplotlib import pyplot as plt
+import sklearn.pipeline # noqa
+import sklearn.preprocessing
+import sklearn.svm # noqa
from skimage.metrics import structural_similarity as compare_ssim
-from .. import __rootdir__
-from . import typealias as tp
-from .image import cropimg
-from .log import logger
+from arknights_mower import __rootdir__
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.image import cropimg
+from arknights_mower.utils.log import logger
-MATCHER_DEBUG = False
-FLANN_INDEX_KDTREE = 0
GOOD_DISTANCE_LIMIT = 0.7
-SIFT = cv2.SIFT_create()
-with open(f'{__rootdir__}/models/svm.model', 'rb') as f:
+
+ORB = cv2.ORB_create(nfeatures=100000, edgeThreshold=0)
+ORB_no_pyramid = cv2.ORB_create(nfeatures=100000, edgeThreshold=0, nlevels=1)
+
+
+def keypoints_scale_invariant(img: tp.GrayImage):
+ return ORB.detectAndCompute(img, None)
+
+
+def keypoints(img: tp.GrayImage):
+ return ORB_no_pyramid.detectAndCompute(img, None)
+
+
+with lzma.open(f"{__rootdir__}/models/svm.model", "rb") as f:
SVC = pickle.loads(f.read())
+# build FlannBasedMatcher
+
+# FLANN_INDEX_KDTREE = 1
+FLANN_INDEX_LSH = 6
+
+index_params = dict(
+ algorithm=FLANN_INDEX_LSH,
+ table_number=6, # 12
+ key_size=12, # 20
+ multi_probe_level=0, # 2
+)
+search_params = dict(checks=50) # 100
+flann = cv2.FlannBasedMatcher(index_params, search_params)
+
+
def getHash(data: list[float]) -> tp.Hash:
- """ calc image hash """
+ """calc image hash"""
avreage = np.mean(data)
return np.where(data > avreage, 1, 0)
def hammingDistance(hash1: tp.Hash, hash2: tp.Hash) -> int:
- """ calc Hamming distance between two hash """
+ """calc Hamming distance between two hash"""
return np.count_nonzero(hash1 != hash2)
def aHash(img1: tp.GrayImage, img2: tp.GrayImage) -> int:
- """ calc image hash """
- data1 = cv2.resize(img1, (8, 8)).flatten()
- data2 = cv2.resize(img2, (8, 8)).flatten()
+ """calc image hash"""
+ data1 = cv2.resize(img1, (8, 4)).flatten()
+ data2 = cv2.resize(img2, (8, 4)).flatten()
hash1 = getHash(data1)
hash2 = getHash(data2)
return hammingDistance(hash1, hash2)
-class Matcher(object):
- """ image matching module """
+class Matcher:
+ """image matching module"""
def __init__(self, origin: tp.GrayImage) -> None:
- logger.debug(f'Matcher init: shape ({origin.shape})')
+ logger.debug(f"Matcher init: shape ({origin.shape})")
self.origin = origin
- self.init_sift()
-
- def init_sift(self) -> None:
- """ get SIFT feature points """
- self.kp, self.des = SIFT.detectAndCompute(self.origin, None)
-
- def match(self, query: tp.GrayImage, draw: bool = False, scope: tp.Scope = None, judge: bool = True) -> Optional(tp.Scope):
- """ check if the image can be matched """
- rect_score = self.score(query, draw, scope) # get matching score
+ self.kp, self.des = keypoints(self.origin)
+
+ def match(
+ self,
+ query: tp.GrayImage,
+ draw: bool = False,
+ scope: tp.Scope = None,
+ dpi_aware: bool = False,
+ prescore: float = 0.0,
+ judge: bool = True,
+ ) -> Optional[tp.Scope]:
+ """check if the image can be matched"""
+ rect_score = self.score(
+ query,
+ draw,
+ scope,
+ only_score=False,
+ dpi_aware=dpi_aware,
+ ) # get matching score
if rect_score is None:
return None # failed in matching
else:
rect, score = rect_score
- # use SVC to determine if the score falls within the legal range
- if judge and not SVC.predict([score])[0]: # numpy.bool_
- logger.debug(f'match fail: {score}')
- return None # failed in matching
- else:
- logger.debug(f'match success: {score}')
- return rect # success in matching
-
- def score(self, query: tp.GrayImage, draw: bool = False, scope: tp.Scope = None, only_score: bool = False) -> Optional(Tuple[tp.Scope, tp.Score]):
- """ scoring of image matching """
+ if prescore > 0:
+ if score[3] >= prescore:
+ logger.debug(f"match success: {rect_score}")
+ return rect
+ else:
+ logger.debug(f"score is not greater than {prescore}: {rect_score}")
+ return None
+ if judge and not SVC.predict([score])[0]:
+ logger.debug(f"match fail: {rect_score}")
+ return None
+ logger.debug(f"match success: {rect_score}")
+ return rect
+
+ def score(
+ self,
+ query: tp.GrayImage,
+ draw: bool = False,
+ scope: tp.Scope = None,
+ only_score: bool = False,
+ dpi_aware: bool = False,
+ ) -> Optional[Tuple[tp.Scope, tp.Score]]:
+ """scoring of image matching"""
try:
# if feature points is empty
if self.des is None:
- logger.debug('feature points is None')
+ logger.debug("feature points is None")
return None
# specify the crop scope
if scope is not None:
ori_kp, ori_des = [], []
for _kp, _des in zip(self.kp, self.des):
- if scope[0][0] <= _kp.pt[0] and scope[0][1] <= _kp.pt[1] and _kp.pt[0] <= scope[1][0] and _kp.pt[1] <= scope[1][1]:
+ if (
+ scope[0][0] <= _kp.pt[0]
+ and scope[0][1] <= _kp.pt[1]
+ and _kp.pt[0] <= scope[1][0]
+ and _kp.pt[1] <= scope[1][1]
+ ):
ori_kp.append(_kp)
ori_des.append(_des)
- logger.debug(
- f'match crop: {scope}, {len(self.kp)} -> {len(ori_kp)}')
+ logger.debug(f"match crop: {scope}, {len(self.kp)} -> {len(ori_kp)}")
ori_kp, ori_des = np.array(ori_kp), np.array(ori_des)
else:
ori_kp, ori_des = self.kp, self.des
# if feature points is less than 2
if len(ori_kp) < 2:
- logger.debug('feature points is less than 2')
+ logger.debug("feature points is less than 2")
return None
# the height & width of query image
h, w = query.shape
# the feature point of query image
- qry_kp, qry_des = SIFT.detectAndCompute(query, None)
+ if dpi_aware:
+ qry_kp, qry_des = keypoints_scale_invariant(query)
+ else:
+ qry_kp, qry_des = keypoints(query)
- # build FlannBasedMatcher
- index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
- search_params = dict(checks=50)
- flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(qry_des, ori_des, k=2)
# store all the good matches as per Lowe's ratio test
good = []
- for x, y in matches:
- if x.distance < GOOD_DISTANCE_LIMIT * y.distance:
- good.append(x)
+ for pair in matches:
+ if (len_pair := len(pair)) == 2:
+ x, y = pair
+ if x.distance < GOOD_DISTANCE_LIMIT * y.distance:
+ good.append(x)
+ elif len_pair == 1:
+ good.append(pair[0])
good_matches_rate = len(good) / len(qry_des)
# draw all the good matches, for debug
if draw:
- result = cv2.drawMatches(
- query, qry_kp, self.origin, ori_kp, good, None)
- plt.imshow(result, 'gray')
- plt.show()
+ result = cv2.drawMatches(query, qry_kp, self.origin, ori_kp, good, None)
+ from matplotlib import pyplot as plt
+
+ plt.imshow(result)
+ plt.show()
# if the number of good matches no more than 4
if len(good) <= 4:
logger.debug(
- f'not enough good matches are found: {len(good)} / {len(qry_des)}')
+ f"not enough good matches are found: {len(good)} / {len(qry_des)}"
+ )
return None
# get the coordinates of good matches
- qry_pts = np.float32(
- [qry_kp[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
- ori_pts = np.float32(
- [ori_kp[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
+ qry_pts = np.int32([qry_kp[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
+ ori_pts = np.int32([ori_kp[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
# calculated transformation matrix and the mask
- M, mask = cv2.findHomography(qry_pts, ori_pts, cv2.RANSAC, 5.0)
- matchesMask = mask.ravel().tolist()
+ M, mask = cv2.estimateAffine2D(qry_pts, ori_pts, None, cv2.RANSAC)
# if transformation matrix is None
if M is None:
- logger.debug('calculated transformation matrix failed')
+ logger.debug("calculated transformation matrix failed")
return None
+ else:
+ logger.debug(f"transform matrix: {M.tolist()}")
+
+ M[0][1] = 0
+ M[1][0] = 0
+ avg = (M[0][0] + M[1][1]) / 2
+ M[0][0] = avg
+ M[1][1] = avg
# calc the location of the query image
- quad = np.float32([[[0, 0]], [[0, h-1]], [[w-1, h-1]], [[w-1, 0]]])
- quad = cv2.perspectiveTransform(quad, M) # quadrangle
- quad_points = qp = np.int32(quad).reshape(4, 2).tolist()
+ # quad = np.float32([[[0, 0]], [[0, h-1]], [[w-1, h-1]], [[w-1, 0]]])
+ quad = np.int32([[[0, 0]], [[w, h]]])
+ quad = cv2.transform(quad, M) # quadrangle
+ rect = quad.reshape(2, 2).tolist()
# draw the result, for debug
- if draw or MATCHER_DEBUG:
- cv2.polylines(self.origin, [np.int32(quad)],
- True, 0, 2, cv2.LINE_AA)
- draw_params = dict(matchColor=(0, 255, 0), singlePointColor=None,
- matchesMask=matchesMask, flags=2)
- result = cv2.drawMatches(query, qry_kp, self.origin, ori_kp,
- good, None, **draw_params)
- plt.imshow(result, 'gray')
+ if draw:
+ matchesMask = mask.ravel().tolist()
+ origin_copy = cv2.cvtColor(self.origin, cv2.COLOR_GRAY2RGB)
+ cv2.rectangle(origin_copy, rect[0], rect[1], (255, 0, 0), 3)
+ draw_params = dict(
+ matchColor=(0, 255, 0),
+ singlePointColor=None,
+ matchesMask=matchesMask,
+ flags=2,
+ )
+ result = cv2.drawMatches(
+ query, qry_kp, origin_copy, ori_kp, good, None, **draw_params
+ )
+ plt.imshow(result)
plt.show()
- # if quadrangle is not rectangle
- if max(abs(qp[0][0] - qp[1][0]), abs(qp[2][0] - qp[3][0]), abs(qp[0][1] - qp[3][1]), abs(qp[1][1] - qp[2][1])) > 30:
- logger.debug(f'square is not rectangle: {qp}')
- return None
-
- # make quadrangle rectangle
- rect = [(np.min(quad[:, 0, 0]), np.min(quad[:, 0, 1])),
- (np.max(quad[:, 0, 0]), np.max(quad[:, 0, 1]))]
+ min_width = max(10, 0 if dpi_aware else w * 0.8)
+ min_height = max(10, 0 if dpi_aware else h * 0.8)
# if rectangle is too small
- if rect[1][0] - rect[0][0] < 10 or rect[1][1] - rect[0][1] < 10:
- logger.debug(f'rectangle is too small: {rect}')
+ if (
+ rect[1][0] - rect[0][0] < min_width
+ or rect[1][1] - rect[0][1] < min_height
+ ):
+ logger.debug(f"rectangle is too small: {rect}")
return None
+ if not dpi_aware:
+ max_width = w * 1.25
+ if rect[1][0] - rect[0][0] > max_width:
+ logger.debug(f"rectangle is too big: {rect}")
+ return None
+
# measure the rate of good match within the rectangle (x-axis)
better = filter(
- lambda m:
- rect[0][0] < ori_kp[m.trainIdx].pt[0] < rect[1][0] and rect[0][1] < ori_kp[m.trainIdx].pt[1] < rect[1][1], good)
+ lambda m: rect[0][0] < ori_kp[m.trainIdx].pt[0] < rect[1][0]
+ and rect[0][1] < ori_kp[m.trainIdx].pt[1] < rect[1][1],
+ good,
+ )
better_kp_x = [qry_kp[m.queryIdx].pt[0] for m in better]
if len(better_kp_x):
good_area_rate = np.ptp(better_kp_x) / w
@@ -189,23 +261,23 @@ def score(self, query: tp.GrayImage, draw: bool = False, scope: tp.Scope = None,
rect_img = cropimg(self.origin, rect)
# if rect_img is too small
- if np.min(rect_img.shape) < 10:
- logger.debug(f'rect_img is too small: {rect_img.shape}')
+ if rect_img.shape[0] < min_height or rect_img.shape[1] < min_width:
+ logger.debug(f"rect_img is too small: {rect_img.shape}")
return None
# transpose rect_img
rect_img = cv2.resize(rect_img, query.shape[::-1])
# draw the result
- if draw or MATCHER_DEBUG:
+ if draw:
plt.subplot(1, 2, 1)
- plt.imshow(query, 'gray')
+ plt.imshow(query, cmap="gray", vmin=0, vmax=255)
plt.subplot(1, 2, 2)
- plt.imshow(rect_img, 'gray')
+ plt.imshow(rect_img, cmap="gray", vmin=0, vmax=255)
plt.show()
# calc aHash between query image and rect_img
- hash = 1 - (aHash(query, rect_img) / 32)
+ hash = 1 - (aHash(query, rect_img) / 16)
# calc ssim between query image and rect_img
ssim = compare_ssim(query, rect_img, multichannel=True)
@@ -217,5 +289,4 @@ def score(self, query: tp.GrayImage, draw: bool = False, scope: tp.Scope = None,
return rect, (good_matches_rate, good_area_rate, hash, ssim)
except Exception as e:
- logger.error(e)
- logger.debug(traceback.format_exc())
+ logger.exception(e)
diff --git a/arknights_mower/utils/network.py b/arknights_mower/utils/network.py
new file mode 100644
index 000000000..98af94e86
--- /dev/null
+++ b/arknights_mower/utils/network.py
@@ -0,0 +1,13 @@
+import socket
+
+
+def is_port_in_use(port: int) -> bool:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.settimeout(0.1)
+ return s.connect_ex(("localhost", port)) == 0
+
+
+def get_new_port() -> int:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.bind(("localhost", 0))
+ return s.getsockname()[1]
diff --git a/arknights_mower/utils/news.py b/arknights_mower/utils/news.py
new file mode 100644
index 000000000..6146686c7
--- /dev/null
+++ b/arknights_mower/utils/news.py
@@ -0,0 +1,42 @@
+import re
+from datetime import datetime
+
+import requests
+from bs4 import BeautifulSoup
+
+from arknights_mower.utils.log import logger
+
+
+def get_update_time():
+ host = "https://ak.hypergryph.com"
+ url = host + "/news/"
+ response = requests.get(url)
+ response.encoding = "utf8"
+ soup = BeautifulSoup(response.text, "lxml")
+ soup.encode("utf-8")
+
+ for h1 in soup.find_all("h1"):
+ if "闪断更新公告" in h1.text:
+ pattern = r"(\d+)月(\d+)日(\d+):(\d+)"
+ result = re.findall(pattern, h1.text)[0]
+ result = list(map(int, result))
+ now = datetime.now()
+ update_time = datetime(now.year, result[0], result[1], result[2], result[3])
+ logger.debug(f"闪断更新时间:{update_time}")
+
+ if now > update_time:
+ logger.info("闪断更新时间已过")
+ else:
+ delta = update_time - now
+ msg = "距离闪断更新的时间:"
+ if delta.days > 0:
+ msg += f"{delta.days}天{delta.seconds // 3600}小时"
+ else:
+ h = delta.seconds // 3600
+ m = (delta.seconds - h * 3600) // 60
+ msg += f"{h}小时{m}分钟"
+ link = h1.parent["href"]
+ msg += ";更新公告:" + host + link
+ logger.info(msg)
+ return update_time
+ return None
diff --git a/arknights_mower/utils/operators.py b/arknights_mower/utils/operators.py
new file mode 100644
index 000000000..93da13e05
--- /dev/null
+++ b/arknights_mower/utils/operators.py
@@ -0,0 +1,802 @@
+import copy
+from datetime import datetime, timedelta
+
+from evalidate import Expr, base_eval_model
+
+from arknights_mower.utils.plan import PlanConfig
+
+from ..data import agent_arrange_order, agent_list, base_room_list
+from ..solvers.record import save_action_to_sqlite_decorator
+from ..utils.log import logger
+
+
+class SkillUpgradeSupport:
+ support_class = None
+ level = 1
+ efficiency = 0
+ half_off = False
+ add_on = False
+ match = False
+ use_booster = True
+ name = ""
+ swap_name = ""
+
+ def __init__(self, name, skill_level, efficiency, match, swap_name="艾丽妮"):
+ self.name = name
+ self.level = skill_level
+ self.efficiency = efficiency
+ self.match = match
+ if self.level > 1:
+ self.half_off = True
+ self.swap_name = swap_name
+
+
+class Operators:
+ config = None
+ operators = None
+ exhaust_agent = []
+ exhaust_group = []
+ groups = None
+ dorm = []
+ plan = None
+
+ global_plan = None
+ plan_condition = []
+ shadow_copy = {}
+ current_room_changed_callback = None
+ first_init = True
+ skill_upgrade_supports = []
+
+ def __init__(self, plan):
+ self.operators = {}
+ self.groups = {}
+ self.exhaust_agent = []
+ self.exhaust_group = []
+ self.dorm = []
+ self.workaholic_agent = []
+ self.free_blacklist = []
+ self.global_plan = plan
+ self.backup_plans = plan["backup_plans"]
+ # 切换默认排班
+ self.swap_plan([False] * (len(self.backup_plans)))
+ self.run_order_rooms = {}
+ self.clues = []
+ self.current_room_changed_callback = None
+ self.party_time = None
+
+ self.eval_model = base_eval_model.clone()
+ self.eval_model.nodes.extend(["Call", "Attribute"])
+ self.eval_model.attributes.extend(
+ [
+ "operators",
+ "party_time",
+ "is_working",
+ "is_resting",
+ "current_mood",
+ "current_room",
+ ]
+ )
+
+ def __repr__(self):
+ return f"Operators(operators={self.operators})"
+
+ def calculate_switch_time(self, support: SkillUpgradeSupport):
+ hour = 0
+ half_off = support.half_off
+ level = support.level
+ match = support.match
+ efficiency = support.efficiency
+ same = support.name == support.swap_name
+ if level == 1:
+ half_off = False
+ # if left_minutes > 0 or left_hours > 0:
+ # hour = left_minutes / 60 + left_hours
+ # 基本5%
+ basic = 5
+ if support.add_on:
+ # 阿斯卡伦
+ basic += 5
+ if hour == 0:
+ hour = level * 8
+ if half_off:
+ hour = hour / 2
+ left = 0
+ if not same:
+ left = 5 * (100 + basic + (30 if match else 0)) / 100
+ left = hour - left
+ else:
+ left = hour
+ return left * 100 / (100 + efficiency + basic)
+
+ def swap_plan(self, condition, refresh=False):
+ self.plan = copy.deepcopy(self.global_plan["default_plan"].plan)
+ self.config: PlanConfig = copy.deepcopy(self.global_plan["default_plan"].config)
+ for index, success in enumerate(condition):
+ if success:
+ self.plan, self.config = self.merge_plan(index, self.config, self.plan)
+ self.plan_condition = condition
+ if refresh:
+ self.first_init = True
+ error = self.init_and_validate(True)
+ self.first_init = False
+ if error:
+ return error
+
+ def merge_plan(self, idx, ext_config, default_plan=None):
+ if default_plan is None:
+ default_plan = copy.deepcopy(self.global_plan["default_plan"].plan)
+ plan = copy.deepcopy(self.global_plan["backup_plans"][idx])
+ # 更新切换排班表
+ for key, value in plan.plan.items():
+ if key in default_plan:
+ for idx, operator in enumerate(value):
+ if operator.agent != "Current":
+ default_plan[key][idx] = operator
+ return default_plan, ext_config.merge_config(plan.config)
+
+ def generate_conditions(self, n):
+ if n == 1:
+ return [[True], [False]]
+ else:
+ prev_conditions = self.generate_conditions(n - 1)
+ conditions = []
+ for condition in prev_conditions:
+ conditions.append(condition + [True])
+ conditions.append(condition + [False])
+ return conditions
+
+ def init_and_validate(self, update=False):
+ self.groups = {}
+ self.exhaust_agent = []
+ self.exhaust_group = []
+ self.workaholic_agent = []
+ self.shadow_copy = copy.deepcopy(self.operators)
+ self.operators = {}
+ for room in self.plan.keys():
+ for idx, data in enumerate(self.plan[room]):
+ if data.agent not in agent_list and data.agent != "Free":
+ return f"干员名输入错误: 房间->{room}, 干员->{data.agent}"
+ if data.agent in ["龙舌兰", "但书", "佩佩"]:
+ return f"高效组不可用龙舌兰,但书,佩佩 房间->{room}, 干员->{data.agent}"
+ if data.agent == "菲亚梅塔" and idx == 1:
+ return f"菲亚梅塔不能安排在2号位置 房间->{room}, 干员->{data.agent}"
+ if data.agent == "菲亚梅塔" and not room.startswith("dorm"):
+ return "菲亚梅塔必须安排在宿舍"
+ if data.agent == "Free" and not room.startswith("dorm"):
+ return f"Free只能安排在宿舍 房间->{room}, 干员->{data.agent}"
+ if data.agent in self.operators and data.agent != "Free":
+ return f"高效组干员不可重复 房间->{room},{self.operators[data.agent].room}, 干员->{data.agent}"
+ self.add(
+ Operator(
+ data.agent,
+ room,
+ idx,
+ data.group,
+ data.replacement,
+ "high",
+ operator_type="high",
+ )
+ )
+ missing_replacements = []
+ for room in self.plan.keys():
+ if room.startswith("dorm") and len(self.plan[room]) != 5:
+ return f"宿舍 {room} 人数少于5人"
+ for idx, data in enumerate(self.plan[room]):
+ # 菲亚梅塔替换组做特例判断
+ if (
+ sum(
+ [
+ any(
+ char in replacement_str
+ for replacement_str in data.replacement
+ )
+ for char in ["龙舌兰", "但书", "佩佩"]
+ ]
+ )
+ > 1
+ ):
+ return f"替换组不可同时安排龙舌兰, 但书或者佩佩 房间->{room}, 干员->{data.agent}"
+ if "菲亚梅塔" in data.replacement:
+ return f"替换组不可安排菲亚梅塔 房间->{room}, 干员->{data.agent}"
+ r_count = len(data.replacement)
+ if any(
+ char in replacement_str
+ for replacement_str in data.replacement
+ for char in ["龙舌兰", "但书", "佩佩"]
+ ):
+ r_count -= 1
+ if r_count <= 0 and (
+ (data.agent != "Free" and (not room.startswith("dorm")))
+ or data.agent == "菲亚梅塔"
+ ):
+ missing_replacements.append(data.agent)
+ for _replacement in data.replacement:
+ if _replacement not in agent_list and data.agent != "Free":
+ return f"干员名输入错误: 房间->{room}, 干员->{_replacement}"
+ if data.agent != "菲亚梅塔":
+ # 普通替换
+ if (
+ _replacement in self.operators
+ and self.operators[_replacement].is_high()
+ ):
+ return f"替换组不可用高效组干员: 房间->{room}, 干员->{_replacement}"
+ self.add(Operator(_replacement, ""))
+ else:
+ if _replacement not in self.operators:
+ return f"菲亚梅塔替换不在高效组列: 房间->{room}, 干员->{_replacement}"
+ if (
+ _replacement in self.operators
+ and not self.operators[_replacement].is_high()
+ ):
+ return f"菲亚梅塔替换只能为高效组干员: 房间->{room}, 干员->{_replacement}"
+ # 判定替换缺失
+ if "菲亚梅塔" in missing_replacements:
+ return "菲亚梅塔替换缺失"
+ if len(missing_replacements):
+ return f'以下干员替换组缺失:{",".join(missing_replacements)}'
+ dorm_names = [k for k in self.plan.keys() if k.startswith("dorm")]
+ dorm_names.sort(key=lambda d: d, reverse=False)
+ added = []
+ # 竖向遍历出效率高到低
+ if not update:
+ for dorm in dorm_names:
+ free_found = False
+ for _idx, _dorm in enumerate(self.plan[dorm]):
+ if _dorm.agent == "Free" and _idx <= 1:
+ if "波登可" not in [_agent.agent for _agent in self.plan[dorm]]:
+ return "宿舍必须安排2个宿管"
+ if _dorm.agent != "Free" and free_found:
+ return "Free必须连续且安排在宿管后"
+ if (
+ _dorm.agent == "Free"
+ and not free_found
+ and (dorm + str(_idx)) not in added
+ and len(added) < self.config.max_resting_count
+ ):
+ self.dorm.append(Dormitory((dorm, _idx)))
+ added.append(dorm + str(_idx))
+ free_found = True
+ continue
+ if not free_found:
+ return "宿舍必须安排至少一个Free"
+ # VIP休息位用完后横向遍历
+ for dorm in dorm_names:
+ for _idx, _dorm in enumerate(self.plan[dorm]):
+ if _dorm.agent == "Free" and (dorm + str(_idx)) not in added:
+ self.dorm.append(Dormitory((dorm, _idx)))
+ added.append(dorm + str(_idx))
+ else:
+ for key, value in self.shadow_copy.items():
+ if key not in self.operators:
+ self.add(Operator(key, ""))
+ if len(self.dorm) < self.config.max_resting_count:
+ return f"宿舍Free总数 {len(self.dorm)}小于最大分组数 {self.config.max_resting_count}"
+ # 跑单
+ for x, y in self.plan.items():
+ if not x.startswith("room"):
+ continue
+ if any(
+ char in obj.replacement
+ for obj in y
+ for char in ["但书", "龙舌兰", "佩佩"]
+ ):
+ self.run_order_rooms[x] = {}
+ # 判定分组排班可能性
+ current_high = self.config.max_resting_count
+ current_low = len(self.dorm) - self.config.max_resting_count
+ for key in self.groups:
+ high_count = 0
+ low_count = 0
+ _replacement = []
+ for name in self.groups[key]:
+ _candidate = next(
+ (
+ r
+ for r in self.operators[name].replacement
+ if r not in _replacement and r not in ["龙舌兰", "但书", "佩佩"]
+ ),
+ None,
+ )
+ if _candidate is None:
+ return f"{key} 分组无法排班,替换组数量不够"
+ else:
+ _replacement.append(_candidate)
+ if self.operators[name].workaholic:
+ continue
+ if self.operators[name].resting_priority == "high":
+ high_count += 1
+ else:
+ low_count += 1
+ if high_count > current_high or low_count > current_low:
+ return f"{key} 分组无法排班,宿舍可用高优先{current_high},低优先{current_low}->分组需要高优先{high_count},低优先{low_count}"
+ # 设定令夕模式的心情阈值
+ self.init_mood_limit()
+ for name in self.workaholic_agent:
+ if name not in self.config.free_blacklist:
+ self.config.free_blacklist.append(name)
+ logger.info("宿舍黑名单:" + str(self.config.free_blacklist))
+
+ def set_mood_limit(self, name, upper_limit=24, lower_limit=0):
+ if name in self.operators:
+ self.operators[name].upper_limit = upper_limit
+ self.operators[name].lower_limit = lower_limit
+ logger.info(f"自动设置{name}心情下限为{lower_limit},上限为{upper_limit}")
+
+ def init_mood_limit(self):
+ # 设置心情阈值 for 夕,令,
+ if self.config.ling_xi == 1:
+ self.set_mood_limit("令", upper_limit=12)
+ self.set_mood_limit("夕", lower_limit=12)
+ elif self.config.ling_xi == 2:
+ self.set_mood_limit("夕", upper_limit=12)
+ self.set_mood_limit("令", lower_limit=12)
+ elif self.config.ling_xi == 0:
+ self.set_mood_limit("夕")
+ self.set_mood_limit("令")
+ # 设置同组心情阈值
+ finished = []
+ for name in ["夕", "令"]:
+ if (
+ name in self.operators
+ and self.operators[name].group != ""
+ and self.operators[name].group not in finished
+ ):
+ for group_name in self.groups[self.operators[name].group]:
+ if group_name not in ["夕", "令"]:
+ if self.config.ling_xi in [1, 2]:
+ self.set_mood_limit(group_name, lower_limit=12)
+ elif self.config.ling_xi == 0:
+ self.set_mood_limit(group_name, lower_limit=0)
+ finished.append(self.operators[name].group)
+
+ # 设置铅踝心情阈值
+ # 三种情况:
+ # 1. 铅踝不是主力:不管
+ # 2. 铅踝是红云组主力,设置心情上限 12、下限 8,效率 37%
+ # 3. 铅踝是普通主力:设置心情下限 20,效率 30%
+ TOTTER = "铅踝"
+ VERMEIL = "红云"
+ if TOTTER in self.operators and self.operators[TOTTER].operator_type == "high":
+ if (
+ VERMEIL in self.operators
+ and self.operators[VERMEIL].operator_type == "high"
+ and self.operators[VERMEIL].room == self.operators[TOTTER].room
+ ):
+ self.set_mood_limit(TOTTER, upper_limit=12, lower_limit=8)
+ else:
+ self.set_mood_limit(TOTTER, upper_limit=24, lower_limit=20)
+
+ def evaluate_expression(self, expression):
+ try:
+ result = Expr(expression, self.eval_model).eval({"op_data": self})
+ return result
+ except Exception as e:
+ logger.exception(f"Error evaluating expression: {e}")
+ return None
+
+ def get_current_room(self, room, bypass=False, current_index=None):
+ room_data = {
+ v.current_index: v
+ for k, v in self.operators.items()
+ if v.current_room == room
+ }
+ res = [obj.agent for obj in self.plan[room]]
+ not_found = False
+ for idx, op in enumerate(res):
+ if idx in room_data:
+ res[idx] = room_data[idx].name
+ else:
+ res[idx] = ""
+ if current_index is not None and idx not in current_index:
+ continue
+ not_found = True
+ if not_found and not bypass:
+ return None
+ else:
+ return res
+
+ def predict_fia(self, operators, fia_mood, hours=240):
+ recover_hours = (24 - fia_mood) / 2
+ for agent in operators:
+ agent.mood -= agent.depletion_rate * recover_hours
+ if agent.mood < 0.0:
+ return False
+ if recover_hours >= hours or 0 < recover_hours < 1:
+ return True
+ operators.sort(
+ key=lambda x: (x.mood - x.lower_limit) / (x.upper_limit - x.lower_limit),
+ reverse=False,
+ )
+ fia_mood = operators[0].mood
+ operators[0].mood = 24
+ return self.predict_fia(operators, fia_mood, hours - recover_hours)
+
+ def reset_dorm_time(self):
+ for name in self.operators.keys():
+ agent = self.operators[name]
+ if agent.room.startswith("dorm"):
+ agent.time_stamp = None
+
+ @save_action_to_sqlite_decorator
+ def update_detail(self, name, mood, current_room, current_index, update_time=False):
+ agent = self.operators[name]
+ if update_time:
+ if agent.time_stamp is not None and agent.mood > mood:
+ agent.depletion_rate = (
+ (agent.mood - mood)
+ * 3600
+ / ((datetime.now() - agent.time_stamp).total_seconds())
+ )
+ agent.time_stamp = datetime.now()
+ # 如果移出宿舍,则清除对应宿舍数据 且重新记录高效组心情(如果有备用班,则跳过高效组判定)
+ if (
+ agent.current_room.startswith("dorm")
+ and not current_room.startswith("dorm")
+ and (agent.is_high() or self.backup_plans)
+ ):
+ self.refresh_dorm_time(
+ agent.current_room, agent.current_index, {"agent": ""}
+ )
+ if update_time:
+ self.time_stamp = datetime.now()
+ else:
+ self.time_stamp = None
+ agent.depletion_rate = 0
+ if (
+ self.get_dorm_by_name(name)[0] is not None
+ and not current_room.startswith("dorm")
+ and (agent.is_high() or self.backup_plans)
+ ):
+ _dorm = self.get_dorm_by_name(name)[1]
+ _dorm.name = ""
+ _dorm.time = None
+ agent.current_room = current_room
+ agent.current_index = current_index
+ agent.mood = mood
+ # 如果是高效组且没有记录时间,则返还index
+ if agent.current_room.startswith("dorm") and (
+ agent.is_high() or self.backup_plans
+ ):
+ for dorm in self.dorm:
+ if (
+ dorm.position[0] == current_room
+ and dorm.position[1] == current_index
+ and dorm.time is None
+ ):
+ return current_index
+ if agent.name == "菲亚梅塔" and (
+ self.operators["菲亚梅塔"].time_stamp is None
+ or self.operators["菲亚梅塔"].time_stamp < datetime.now()
+ ):
+ return current_index
+
+ def refresh_dorm_time(self, room, index, agent):
+ for idx, dorm in enumerate(self.dorm):
+ # Filter out resting priority low
+ # if idx >= self.config.max_resting_count:
+ # break
+ if dorm.position[0] == room and dorm.position[1] == index:
+ # 如果人为高效组,则记录时间
+ _name = agent["agent"]
+ if _name in self.operators.keys() and (
+ self.operators[_name].is_high() or self.config.free_room
+ ):
+ dorm.name = _name
+ _agent = self.operators[_name]
+ # 如果干员有心情上限,则按比例修改休息时间
+ if _agent.mood != 24:
+ sec_remaining = (
+ (_agent.upper_limit - _agent.mood)
+ * ((agent["time"] - _agent.time_stamp).total_seconds())
+ / (24 - _agent.mood)
+ )
+ dorm.time = _agent.time_stamp + timedelta(seconds=sec_remaining)
+ else:
+ dorm.time = agent["time"]
+ elif _name in agent_list:
+ dorm.name = _name
+ dorm.time = agent["time"]
+ break
+
+ def correct_dorm(self):
+ for idx, dorm in enumerate(self.dorm):
+ if dorm.name != "" and dorm.name in self.operators.keys():
+ op = self.operators[dorm.name]
+ if not (
+ dorm.position[0] == op.current_room
+ and dorm.position[1] == op.current_index
+ ):
+ self.dorm[idx].name = ""
+ self.dorm[idx].time = None
+ else:
+ if (
+ self.dorm[idx].time is not None
+ and self.dorm[idx].time < datetime.now()
+ ):
+ op.mood = op.upper_limit
+ op.time_stamp = self.dorm[idx].time
+ logger.debug(
+ f"检测到{op.name}心情恢复满,设置心情至{op.upper_limit}"
+ )
+
+ def get_train_support(self):
+ for name in self.operators.keys():
+ agent = self.operators[name]
+ if agent.current_room == "train" and agent.current_index == 0:
+ return agent.name
+ return None
+
+ def get_refresh_index(self, room, plan):
+ ret = []
+ if room.startswith("dorm") and self.config.free_room:
+ return [i for i, x in enumerate(self.plan[room]) if x == "Free"]
+ for idx, dorm in enumerate(self.dorm):
+ # Filter out resting priority low
+ if idx >= self.config.max_resting_count:
+ if not self.config.free_room:
+ break
+ if dorm.position[0] == room:
+ for i, _name in enumerate(plan):
+ if _name not in self.operators.keys():
+ self.add(Operator(_name, ""))
+ if not self.config.free_room:
+ if self.operators[_name].is_high() and not self.operators[
+ _name
+ ].room.startswith("dorm"):
+ ret.append(i)
+ elif not self.operators[_name].room.startswith("dorm"):
+ ret.append(i)
+ break
+ return ret
+
+ def get_dorm_by_name(self, name):
+ for idx, dorm in enumerate(self.dorm):
+ if dorm.name == name:
+ return idx, dorm
+ return None, None
+
+ def add(self, operator):
+ if operator.name not in agent_list:
+ return
+ if self.config.is_resting_priority(operator.name):
+ operator.resting_priority = "low"
+ operator.exhaust_require = self.config.is_exhaust_require(operator.name)
+ operator.rest_in_full = self.config.is_rest_in_full(operator.name)
+ operator.workaholic = self.config.is_workaholic(operator.name)
+ operator.refresh_order_room = self.config.is_refresh_trading(operator.name)
+ if operator.name in agent_arrange_order:
+ operator.arrange_order = agent_arrange_order[operator.name]
+ # 复制基建数据
+ if operator.name in self.shadow_copy:
+ exist = self.shadow_copy[operator.name]
+ operator.mood = exist.mood
+ operator.time_stamp = exist.time_stamp
+ operator.depletion_rate = exist.depletion_rate
+ operator.current_room = exist.current_room
+ operator.current_index = exist.current_index
+ self.operators[operator.name] = operator
+ # 需要用尽心情干员逻辑
+ if (
+ operator.exhaust_require or operator.group in self.exhaust_group
+ ) and operator.name not in self.exhaust_agent:
+ self.exhaust_agent.append(operator.name)
+ if operator.group != "":
+ self.exhaust_group.append(operator.group)
+ # 干员分组逻辑
+ if operator.group != "":
+ if operator.group not in self.groups.keys():
+ self.groups[operator.group] = [operator.name]
+ else:
+ self.groups[operator.group].append(operator.name)
+ if operator.workaholic and operator.name not in self.workaholic_agent:
+ self.workaholic_agent.append(operator.name)
+
+ def available_free(self, free_type="high"):
+ ret = 0
+ freeName = []
+ if free_type == "high":
+ idx = 0
+ for dorm in self.dorm:
+ if dorm.name == "" or (
+ dorm.name in self.operators.keys()
+ and not self.operators[dorm.name].is_high()
+ ):
+ ret += 1
+ elif dorm.time is not None and dorm.time < datetime.now():
+ logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位")
+ freeName.append(dorm.name)
+ ret += 1
+ if idx == self.config.max_resting_count - 1:
+ break
+ else:
+ idx += 1
+ else:
+ idx = self.config.max_resting_count
+ for i in range(idx, len(self.dorm)):
+ dorm = self.dorm[i]
+ # 释放满休息位
+ # TODO 高效组且低优先可以相互替换
+ if dorm.name == "" or (
+ dorm.name in self.operators.keys()
+ and not self.operators[dorm.name].is_high()
+ ):
+ ret += 1
+ elif dorm.time is not None and dorm.time < datetime.now():
+ logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位")
+ freeName.append(dorm.name)
+ ret += 1
+ if len(freeName) > 0:
+ for name in freeName:
+ if name in agent_list:
+ self.operators[name].mood = self.operators[name].upper_limit
+ self.operators[name].depletion_rate = 0
+ self.operators[name].time_stamp = datetime.now()
+ return ret
+
+ def assign_dorm(self, name):
+ is_high = self.operators[name].resting_priority == "high"
+ if is_high:
+ _room = next(
+ obj
+ for obj in self.dorm
+ if obj.name not in self.operators.keys()
+ or not self.operators[obj.name].is_high()
+ )
+ else:
+ _room = None
+ for i in range(self.config.max_resting_count, len(self.dorm)):
+ _name = self.dorm[i].name
+ if _name == "" or not self.operators[_name].is_high():
+ _room = self.dorm[i]
+ break
+ _room.name = name
+ return _room
+
+ def get_current_operator(self, room, index):
+ for key, value in self.operators.items():
+ if value.current_room == room and value.current_index == index:
+ return value
+ return None
+
+ def print(self):
+ ret = "{"
+ op = []
+ dorm = []
+ for k, v in self.operators.items():
+ op.append("'" + k + "': " + str(vars(v)))
+ ret += "'operators': {" + ",".join(op) + "},"
+ for v in self.dorm:
+ dorm.append(str(vars(v)))
+ ret += "'dorms': [" + ",".join(dorm) + "]}"
+ return ret
+
+
+class Dormitory:
+ def __init__(self, position, name="", time=None):
+ self.position = position
+ self.name = name
+ self.time = time
+
+ def __repr__(self):
+ return (
+ f"Dormitory(position={self.position},name='{self.name}',time='{self.time}')"
+ )
+
+
+class Operator:
+ time_stamp = None
+ depletion_rate = 0
+ workaholic = False
+ arrange_order = [2, "false"]
+
+ def __init__(
+ self,
+ name,
+ room,
+ index=-1,
+ group="",
+ replacement=[],
+ resting_priority="low",
+ current_room="",
+ exhaust_require=False,
+ mood=24,
+ upper_limit=24,
+ rest_in_full=False,
+ current_index=-1,
+ lower_limit=0,
+ operator_type="low",
+ depletion_rate=0,
+ time_stamp=None,
+ refresh_order_room=None,
+ ):
+ if refresh_order_room is not None:
+ self.refresh_order_room = refresh_order_room
+ self.refresh_order_room = [False, []]
+ self.name = name
+ self.room = room
+ self.operator_type = operator_type
+ self.index = index
+ self.group = group
+ self.replacement = replacement
+ self.resting_priority = resting_priority
+ self._current_room = None
+ self.current_room = current_room
+ self.exhaust_require = exhaust_require
+ self.upper_limit = upper_limit
+ self.rest_in_full = rest_in_full
+ self.mood = mood
+ self.current_index = current_index
+ self.lower_limit = lower_limit
+ self.depletion_rate = depletion_rate
+ self.time_stamp = time_stamp
+
+ @property
+ def current_room(self):
+ return self._current_room
+
+ @current_room.setter
+ def current_room(self, value):
+ if self._current_room != value:
+ self._current_room = value
+ if Operators.current_room_changed_callback and self.refresh_order_room[0]:
+ Operators.current_room_changed_callback(self)
+
+ def is_high(self):
+ return self.operator_type == "high"
+
+ def is_resting(self):
+ return self.current_room.startswith("dorm")
+
+ def is_working(self):
+ return self.current_room in base_room_list and not self.is_resting()
+
+ def need_to_refresh(self, h=2, r=""):
+ # 是否需要读取心情
+ if (
+ self.time_stamp is None
+ or (
+ self.time_stamp is not None
+ and self.time_stamp + timedelta(hours=h) < datetime.now()
+ )
+ or (r.startswith("dorm") and not self.room.startswith("dorm"))
+ ):
+ return True
+
+ def not_valid(self):
+ if self.room == "train":
+ return False
+ if self.operator_type == "high":
+ if self.workaholic:
+ return (
+ self.current_room != self.room or self.index != self.current_index
+ )
+ if not self.room.startswith("dorm") and self.current_room.startswith(
+ "dorm"
+ ):
+ if self.mood == -1 or self.mood == 24:
+ return True
+ else:
+ return False
+ return (
+ self.need_to_refresh(2.5)
+ or self.current_room != self.room
+ or self.index != self.current_index
+ )
+ return False
+
+ def current_mood(self):
+ predict = self.mood
+ if self.time_stamp is not None:
+ predict = (
+ self.mood
+ - self.depletion_rate
+ * (datetime.now() - self.time_stamp).total_seconds()
+ / 3600
+ )
+ if 0 <= predict <= 24:
+ return predict
+ else:
+ return self.mood
+
+ def __repr__(self):
+ return f"Operator(name='{self.name}', room='{self.room}', index={self.index}, group='{self.group}', replacement={self.replacement}, resting_priority='{self.resting_priority}', current_room='{self.current_room}',exhaust_require={self.exhaust_require},mood={self.mood}, upper_limit={self.upper_limit}, rest_in_full={self.rest_in_full}, current_index={self.current_index}, lower_limit={self.lower_limit}, operator_type='{self.operator_type}',depletion_rate={self.depletion_rate},time_stamp='{self.time_stamp}',refresh_order_room = {self.refresh_order_room})"
diff --git a/arknights_mower/utils/param.py b/arknights_mower/utils/param.py
deleted file mode 100644
index 1336b1647..000000000
--- a/arknights_mower/utils/param.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from .typealias import ParamArgs
-
-
-class ParamError(ValueError):
- """ 参数错误 """
-
-
-def parse_operation_params(args: ParamArgs = []):
- level = None
- times = -1
- potion = 0
- originite = 0
- eliminate = 0
-
- try:
- for p in args:
- if p[0] == '-':
- val = -1
- if len(p) > 2:
- val = int(p[2:])
- if p[1] == 'r':
- assert potion == 0
- potion = val
- elif p[1] == 'R':
- assert originite == 0
- originite = val
- elif p[1] == 'e':
- assert eliminate == 0
- eliminate = 1
- elif p[1] == 'E':
- assert eliminate == 0
- eliminate = 2
- elif p.find('-') == -1:
- assert times == -1
- times = int(p)
- else:
- assert level is None
- level = p
- except Exception:
- raise ParamError
- return level, times, potion, originite, eliminate
-
-
-def operation_times(args: ParamArgs = []) -> int:
- _, times, _, _, _ = parse_operation_params(args)
- return times
diff --git a/arknights_mower/utils/path.py b/arknights_mower/utils/path.py
new file mode 100644
index 000000000..fbe7a1506
--- /dev/null
+++ b/arknights_mower/utils/path.py
@@ -0,0 +1,102 @@
+import os
+import sys
+from pathlib import Path
+
+appname = "arknights_mower"
+appauthor = "ArkMower"
+global_space = None
+
+
+def find_git_root(directory: Path) -> Path:
+ if (directory / ".git").is_dir():
+ return directory
+ elif directory == directory.parent:
+ return None
+ else:
+ return find_git_root(directory.parent)
+
+
+# define _app_dir
+if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
+ _internal_dir = Path(sys._MEIPASS).resolve()
+ _install_dir = _internal_dir.parent.resolve()
+ _app_dir = _internal_dir.parent
+else:
+ _app_dir = find_git_root(Path(os.getcwd()).resolve())
+ if not _app_dir:
+ _app_dir = Path(os.getcwd()).resolve()
+ _internal_dir = _app_dir
+ _install_dir = _internal_dir
+
+
+def _get_path(base_path, path, space) -> Path:
+ if space:
+ return Path(base_path) / space / path
+ else:
+ return Path(base_path) / path
+
+
+def get_app_path(path, space=None) -> Path:
+ global global_space
+ if space is None: # 不用 not space 是因为 not '' == True
+ space = global_space
+ return _get_path(_app_dir, path, space)
+
+
+def get_internal_path(path) -> Path:
+ return _get_path(_internal_dir, path, None)
+
+
+def get_install_path(path) -> Path:
+ return _get_path(_install_dir, path, None)
+
+
+def get_path(path: str, space=None) -> Path:
+ """
+ 使用 '@xxx/' 来表示一些特别的目录
+ @app: mower数据文件夹, 例如 get_path('@app/logs/runtime.log')
+ @internal: mower内部文件夹, 在开发时为 .git 所在目录, 打包时为 @app/_internal
+ @install: mower 安装文件夹,在开发时与 @internal 相同,打包时为 mower 的安装目录
+
+ 指定space来区分配置文件空间,如space为None(默认值),则使用global_space
+
+ 特别的,如果要覆盖global_space并指定默认目录,使用space=''
+ """
+ global global_space
+ if space is None:
+ space = global_space
+ path = path.replace("\\", "/")
+
+ if isinstance(path, str) and path.startswith("@"):
+ index = path.find("/")
+ index = index if index != -1 else len(path)
+ special_dir_name = path[1:index]
+ relative_path = path[index:].strip("/")
+ if special_dir_name == "app":
+ return get_app_path(relative_path, space)
+ elif special_dir_name == "internal":
+ return get_internal_path(relative_path)
+ elif special_dir_name == "install":
+ return get_install_path(relative_path)
+ else:
+ raise ValueError(
+ "{}: {} 不是一个有效的特殊目录别名".format(path, special_dir_name)
+ )
+ else:
+ return Path(path)
+ # raise ValueError("{} 路径必须以 '@xxx' 开头".format(path))
+
+
+class SpecialDir:
+ def __init__(self, method):
+ self.method = method
+
+ def __truediv__(self, path) -> Path:
+ return self.method(path, None)
+
+ def __str__(self):
+ return str(self.method("", None))
+
+
+app_dir = SpecialDir(get_app_path)
+internal_dir = SpecialDir(get_internal_path)
diff --git a/arknights_mower/utils/plan.py b/arknights_mower/utils/plan.py
new file mode 100644
index 000000000..f095ffec0
--- /dev/null
+++ b/arknights_mower/utils/plan.py
@@ -0,0 +1,159 @@
+import copy
+from enum import Enum
+from typing import Optional, Self
+
+from arknights_mower.utils.logic_expression import LogicExpression
+
+
+class PlanTriggerTiming(Enum):
+ "副表触发时机"
+
+ BEGINNING = 0
+ "任务开始"
+ BEFORE_PLANNING = 300
+ "下班结束"
+ AFTER_PLANNING = 600
+ "上班结束"
+ END = 999
+ "任务结束"
+
+
+def to_list(str_data: str) -> list[str]:
+ lst = str_data.replace(",", ",").split(",")
+ return [x.strip() for x in lst]
+
+
+class PlanConfig:
+ def __init__(
+ self,
+ rest_in_full: str,
+ exhaust_require: str,
+ resting_priority: str,
+ ling_xi: int = 0,
+ workaholic: str = "",
+ max_resting_count: int = 4,
+ free_blacklist: str = "",
+ resting_threshold: float = 0.5,
+ refresh_trading_config: str = "",
+ free_room: bool = False,
+ ):
+ """排班的设置
+
+ Args:
+ rest_in_full: 回满
+ exhaust_require: 耗尽
+ resting_priority: 低优先级
+ ling_xi: 令夕模式
+ workaholic: 0心情工作
+ max_resting_count: 最大组人数
+ free_blacklist: 宿舍黑名单
+ resting_threshold: 心情阈值
+ refresh_trading_config: 跑单时间刷新干员
+ free_room: 宿舍不养闲人模式
+ """
+ self.rest_in_full = to_list(rest_in_full)
+ self.exhaust_require = to_list(exhaust_require)
+ self.workaholic = to_list(workaholic)
+ self.resting_priority = to_list(resting_priority)
+ self.max_resting_count = max_resting_count
+ self.free_blacklist = to_list(free_blacklist)
+ # 0 为均衡模式
+ # 1 为感知信息模式
+ # 2 为人间烟火模式
+ self.ling_xi = ling_xi
+ self.resting_threshold = resting_threshold
+ self.free_room = free_room
+ # 格式为 干员名字+ 括弧 +指定房间(逗号分隔)
+ # 不指定房间则默认全跑单站
+ # example: 阿米娅,夕,令
+ # 夕(room_3_1,room_1_3),令(room_3_1)
+ self.refresh_trading_config = to_list(refresh_trading_config)
+
+ def is_rest_in_full(self, agent_name) -> bool:
+ return agent_name in self.rest_in_full
+
+ def is_exhaust_require(self, agent_name) -> bool:
+ return agent_name in self.exhaust_require
+
+ def is_workaholic(self, agent_name) -> bool:
+ return agent_name in self.workaholic
+
+ def is_resting_priority(self, agent_name) -> bool:
+ return agent_name in self.resting_priority
+
+ def is_free_blacklist(self, agent_name) -> bool:
+ return agent_name in self.free_blacklist
+
+ def is_refresh_trading(self, agent_name) -> list[bool, list[str]]:
+ match = next(
+ (e for e in self.refresh_trading_config if agent_name in e.lower()),
+ None,
+ )
+ if match is not None:
+ if match.replace(agent_name, "") != "":
+ return [True, match.replace(agent_name, "").split(",")]
+ else:
+ return [True, []]
+ else:
+ return [False, []]
+
+ def merge_config(self, target: Self) -> Self:
+ n = copy.deepcopy(self)
+ for p in [
+ "rest_in_full",
+ "exhaust_require",
+ "workaholic",
+ "resting_priority",
+ "free_blacklist",
+ "refresh_trading_config",
+ ]:
+ p_dict = set(getattr(n, p))
+ target_p = set(getattr(target, p))
+ setattr(n, p, list(p_dict.union(target_p)))
+ return n
+
+
+class Room:
+ def __init__(self, agent: str, group: str, replacement: list[str]):
+ """房间
+
+ Args:
+ agent: 主力干员
+ group: 组
+ replacement: 替换组
+ """
+ self.agent = agent
+ self.group = group
+ self.replacement = replacement
+
+
+class Plan:
+ def __init__(
+ self,
+ plan: dict[str, Room],
+ config: PlanConfig,
+ trigger: Optional[LogicExpression] = None,
+ task: Optional[dict[str, list[str]]] = None,
+ trigger_timing: Optional[str] = None,
+ ):
+ """
+ Args:
+ plan: 基建计划 or 触发备用plan 的排班表,只需要填和默认不一样的部分
+ config: 基建计划相关配置,必须填写全部配置
+ trigger: 触发备用plan 的条件(必填)就是每次最多只有一个备用plan触发
+ task: 触发备用plan 的时间生成的任务(选填)
+ trigger_timing: 触发时机
+ """
+ self.plan = plan
+ self.config = config
+ self.trigger = trigger
+ self.task = task
+ self.trigger_timing = self.set_timing_enum(trigger_timing)
+
+ @staticmethod
+ def set_timing_enum(value: str) -> PlanTriggerTiming:
+ "将字符串转换为副表触发时机"
+ try:
+ return PlanTriggerTiming[value.upper()]
+ except Exception:
+ return PlanTriggerTiming.AFTER_PLANNING
diff --git a/arknights_mower/utils/priority_queue.py b/arknights_mower/utils/priority_queue.py
deleted file mode 100644
index 55f48697b..000000000
--- a/arknights_mower/utils/priority_queue.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import heapq
-
-
-class PriorityQueue(object):
- """
- 基于 heapq 实现的优先队列
- """
-
- def __init__(self):
- self.queue = []
-
- def push(self, data):
- heapq.heappush(self.queue, data)
-
- def pop(self):
- if len(self.queue) == 0:
- return None
- return heapq.heappop(self.queue)
diff --git a/arknights_mower/utils/qrcode.py b/arknights_mower/utils/qrcode.py
new file mode 100644
index 000000000..e5d99205a
--- /dev/null
+++ b/arknights_mower/utils/qrcode.py
@@ -0,0 +1,78 @@
+import json
+from typing import Dict, List, Optional
+from zlib import compress, decompress
+
+from base45 import b45decode, b45encode
+from PIL import Image, ImageChops, ImageDraw
+from pyzbar import pyzbar
+from qrcode.constants import ERROR_CORRECT_L
+from qrcode.main import QRCode
+
+QRCODE_SIZE = 215
+GAP_SIZE = 16
+BLACK = (0, 0, 0)
+WHITE = (255, 255, 255)
+TOP = 40
+BOTTOM = 995
+LEFT = 40
+
+
+def encode(data: str, n: int = 16, theme: str = "light") -> List[Image.Image]:
+ data = b45encode(compress(data.encode("utf-8"), level=9))
+ length = len(data)
+ split: List[bytes] = []
+ for i in range(n):
+ start = length // n * i
+ end = length if i == n - 1 else length // n * (i + 1)
+ split.append(data[start:end])
+ result: List[Image.Image] = []
+ qr = QRCode(error_correction=ERROR_CORRECT_L)
+ fg, bg = (BLACK, WHITE) if theme == "light" else (WHITE, BLACK)
+ for i in split:
+ qr.add_data(i)
+ img: Image.Image = qr.make_image(fill_color=fg, back_color=bg)
+ result.append(trim(img.get_image()))
+ qr.clear()
+ return result
+
+
+def trim(img: Image.Image) -> Image.Image:
+ bg = Image.new(img.mode, img.size, img.getpixel((0, 0)))
+ diff = ImageChops.difference(img, bg)
+ img = img.crop(diff.getbbox())
+ img = img.resize((QRCODE_SIZE, QRCODE_SIZE))
+ return img
+
+
+def export(plan: Dict, img: Image.Image, theme: str = "light") -> Image.Image:
+ qrcode_list = encode(json.dumps(plan), theme=theme)
+ for idx, i in enumerate(qrcode_list[:7]):
+ img.paste(i, (LEFT + idx * (GAP_SIZE + QRCODE_SIZE), TOP))
+ for idx, i in enumerate(qrcode_list[7:14]):
+ img.paste(i, (LEFT + idx * (GAP_SIZE + QRCODE_SIZE), BOTTOM))
+ for idx, i in enumerate(qrcode_list[14:]):
+ img.paste(i, (2520 + idx * (GAP_SIZE + QRCODE_SIZE), BOTTOM))
+ img = img.convert("RGB")
+ return img
+
+
+def decode(img: Image.Image) -> Optional[Dict]:
+ img = img.convert("RGB")
+ if img.getpixel((0, 0)) == BLACK:
+ img = ImageChops.invert(img)
+ result = []
+ while len(data := pyzbar.decode(img)):
+ img1 = ImageDraw.Draw(img)
+ for d in data:
+ if d.quality > 1:
+ continue
+ left = d.rect.left - 2
+ top = d.rect.top - 2
+ right = left + d.rect.width + 5
+ bottom = top + d.rect.height + 5
+ scope = ((left, top), (right, bottom))
+ img1.rectangle(scope, fill=WHITE)
+ result.append(d)
+ result.sort(key=lambda i: (i.rect.top * 2 > img.size[1], i.rect.left))
+ result = b45decode(b"".join([i.data for i in result]))
+ return json.loads(decompress(result).decode("utf-8"))
diff --git a/arknights_mower/utils/rapidocr.py b/arknights_mower/utils/rapidocr.py
new file mode 100644
index 000000000..3ce878daa
--- /dev/null
+++ b/arknights_mower/utils/rapidocr.py
@@ -0,0 +1,9 @@
+engine = None
+
+
+def initialize_ocr(score=0.3):
+ global engine
+ if not engine:
+ from rapidocr_onnxruntime import RapidOCR
+
+ engine = RapidOCR(text_score=score)
diff --git a/arknights_mower/utils/recognize.py b/arknights_mower/utils/recognize.py
index 8bd2fca39..c3e5aaffc 100644
--- a/arknights_mower/utils/recognize.py
+++ b/arknights_mower/utils/recognize.py
@@ -1,276 +1,634 @@
-from __future__ import annotations
-
import time
-from typing import List, Optional
+from typing import List, Optional, Tuple
import cv2
import numpy as np
+from skimage.metrics import structural_similarity
-from .. import __rootdir__
-from . import config, detector
-from . import typealias as tp
-from .device import Device
-from .image import bytes2img, cropimg, loadimg, thres2
-from .log import logger, save_screenshot
-from .matcher import Matcher
-from .scene import Scene, SceneComment
+from arknights_mower import __rootdir__
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.csleep import MowerExit
+from arknights_mower.utils.deprecated import deprecated
+from arknights_mower.utils.device.device import Device
+from arknights_mower.utils.image import bytes2img, cmatch, cropimg, loadres, thres2
+from arknights_mower.utils.log import logger, save_screenshot
+from arknights_mower.utils.matcher import Matcher
+from arknights_mower.utils.scene import Scene, SceneComment
+from arknights_mower.utils.vector import va
class RecognizeError(Exception):
pass
-class Recognizer(object):
-
+class Recognizer:
def __init__(self, device: Device, screencap: bytes = None) -> None:
self.device = device
- self.start(screencap)
+ self.w = 1920
+ self.h = 1080
+ if screencap is None:
+ self.clear()
+ else:
+ self.start(screencap)
+ self._matcher = None
+ self.scene = Scene.UNDEFINED
+ self.loading_time = 0
+ self.LOADING_TIME_LIMIT = 5
+
+ def clear(self):
+ self._screencap = None
+ self._img = None
+ self._gray = None
+ self._matcher = None
+ self.scene = Scene.UNDEFINED
+
+ @property
+ def screencap(self):
+ if self._screencap is None:
+ self.start()
+ return self._screencap
+
+ @property
+ def img(self):
+ if self._img is None:
+ self.start()
+ return self._img
+
+ @property
+ def gray(self):
+ if self._gray is None:
+ self.start()
+ return self._gray
- def start(self, screencap: bytes = None, build: bool = True) -> None:
- """ init with screencap, build matcher """
+ @property
+ def matcher(self):
+ if self._matcher is None:
+ self._matcher = Matcher(self.gray)
+ return self._matcher
+
+ def start(self, screencap: Optional[bytes] = None) -> None:
+ """init with screencap"""
retry_times = config.MAX_RETRYTIME
while retry_times > 0:
try:
if screencap is not None:
- self.screencap = screencap
+ self._screencap = screencap
+ self._img = bytes2img(screencap)
+ self._gray = bytes2img(screencap, True)
else:
- self.screencap = self.device.screencap()
- self.img = bytes2img(self.screencap, False)
- self.gray = bytes2img(self.screencap, True)
- self.h, self.w, _ = self.img.shape
- self.matcher = Matcher(self.gray) if build else None
- self.scene = Scene.UNDEFINED
+ self._screencap, self._img, self._gray = self.device.screencap()
return
except cv2.error as e:
logger.warning(e)
retry_times -= 1
time.sleep(1)
continue
- raise RuntimeError('init Recognizer failed')
+ raise RuntimeError("init Recognizer failed")
- def update(self, screencap: bytes = None, rebuild: bool = True) -> None:
- """ rebuild matcher """
- self.start(screencap, rebuild)
+ def update(self) -> None:
+ if config.stop_mower.is_set():
+ raise MowerExit
+ self.clear()
def color(self, x: int, y: int) -> tp.Pixel:
- """ get the color of the pixel """
+ """get the color of the pixel"""
return self.img[y][x]
+ @deprecated
def save_screencap(self, folder):
- save_screenshot(self.screencap, subdir=f'{folder}/{self.h}x{self.w}')
+ del folder # 兼容2024.05旧版接口
+ save_screenshot(self.screencap)
+
+ def detect_index_scene(self) -> bool:
+ res = loadres("index_nav", True)
+ h, w = res.shape
+ img = cropimg(self.gray, ((25, 17), (25 + w, 17 + h)))
+ img = thres2(img, 250)
+ result = cv2.matchTemplate(img, res, cv2.TM_SQDIFF_NORMED)
+ result = result[0][0]
+ logger.debug(result)
+ return result < 0.1
+
+ def check_current_focus(self):
+ if self.device.check_current_focus():
+ self.update()
+
+ def check_loading_time(self):
+ if self.scene == Scene.CONNECTING:
+ self.loading_time += 1
+ if self.loading_time > 1:
+ logger.debug(f"检测到连续等待{self.loading_time}次")
+ else:
+ self.loading_time = 0
+ if self.loading_time > self.LOADING_TIME_LIMIT:
+ logger.info(f"检测到连续等待{self.loading_time}次")
+ self.device.exit()
+ time.sleep(3)
+ self.check_current_focus()
+
+ def check_announcement(self):
+ img = cropimg(self.gray, ((960, 0), (1920, 540)))
+ tpl = loadres("announcement_close", True)
+ msk = thres2(tpl, 1)
+ result = cv2.matchTemplate(img, tpl, cv2.TM_SQDIFF_NORMED, None, msk)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ if min_val < 0.02:
+ return (min_loc[0] + 960 + 42, min_loc[1] + 42)
def get_scene(self) -> int:
- """ get the current scene in the game """
+ """get the current scene in the game"""
if self.scene != Scene.UNDEFINED:
return self.scene
- if self.find('connecting', scope=((self.w//2, self.h//10*8), (self.w//4*3, self.h))) is not None:
+
+ # 连接中,优先级最高
+ if self.find("connecting"):
self.scene = Scene.CONNECTING
- elif self.find('index_nav', thres=250, scope=((0, 0), (100+self.w//4, self.h//10))) is not None:
- self.scene = Scene.INDEX
- elif self.find('nav_index') is not None:
+
+ # 平均色匹配
+ elif self.find("confirm"):
+ self.scene = Scene.CONFIRM
+ elif self.find("order_label"):
+ self.scene = Scene.ORDER_LIST
+ elif self.find("drone"):
+ self.scene = Scene.DRONE_ACCELERATE
+ elif self.find("factory_collect"):
+ self.scene = Scene.FACTORY_ROOMS
+ elif self.find("nav_bar"):
self.scene = Scene.NAVIGATION_BAR
- elif self.find('close_mine') is not None:
- self.scene = Scene.CLOSE_MINE
- elif self.find('materiel_ico') is not None:
- self.scene = Scene.MATERIEL
- elif self.find('read_mail') is not None:
+ elif self.find("mail"):
self.scene = Scene.MAIL
- elif self.find('loading') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading2') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading3') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading4') is not None:
- self.scene = Scene.LOADING
- elif self.is_black():
- self.scene = Scene.LOADING
- elif self.find('ope_plan') is not None:
- self.scene = Scene.OPERATOR_BEFORE
- elif self.find('ope_select_start') is not None:
+ elif self.find("navigation/record_restoration"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("fight/refresh"):
+ self.scene = Scene.OPERATOR_SUPPORT
+ elif self.find("ope_select_start"):
self.scene = Scene.OPERATOR_SELECT
- elif self.find('ope_agency_going') is not None:
- self.scene = Scene.OPERATOR_ONGOING
- elif self.find('ope_elimi_finished') is not None:
- self.scene = Scene.OPERATOR_ELIMINATE_FINISH
- elif self.find('ope_finish') is not None:
- self.scene = Scene.OPERATOR_FINISH
- elif self.find('ope_recover_potion_on') is not None:
- self.scene = Scene.OPERATOR_RECOVER_POTION
- elif self.find('ope_recover_originite_on') is not None:
- self.scene = Scene.OPERATOR_RECOVER_ORIGINITE
- elif self.find('double_confirm') is not None:
- if self.find('network_check') is not None:
- self.scene = Scene.NETWORK_CHECK
- else:
- self.scene = Scene.DOUBLE_CONFIRM
- elif self.find('ope_firstdrop') is not None:
- self.scene = Scene.OPERATOR_DROP
- elif self.find('ope_eliminate') is not None:
+ elif self.find("ope_eliminate"):
self.scene = Scene.OPERATOR_ELIMINATE
- elif self.find('ope_elimi_agency_panel') is not None:
+ elif self.find("ope_elimi_agency_panel"):
self.scene = Scene.OPERATOR_ELIMINATE_AGENCY
- elif self.find('ope_giveup') is not None:
- self.scene = Scene.OPERATOR_GIVEUP
- elif self.find('ope_failed') is not None:
- self.scene = Scene.OPERATOR_FAILED
- elif self.find('friend_list_on') is not None:
- self.scene = Scene.FRIEND_LIST_ON
- elif self.find('credit_visiting') is not None:
- self.scene = Scene.FRIEND_VISITING
- elif self.find('infra_overview') is not None:
+ elif self.find("riic/report_title"):
+ self.scene = Scene.RIIC_REPORT
+ elif self.find("control_central_assistants"):
+ self.scene = Scene.CTRLCENTER_ASSISTANT
+ elif self.find("infra_overview"):
self.scene = Scene.INFRA_MAIN
- elif self.find('infra_todo') is not None:
+ elif self.find("infra_todo"):
self.scene = Scene.INFRA_TODOLIST
- elif self.find('clue') is not None:
+ elif self.find("clue"):
self.scene = Scene.INFRA_CONFIDENTIAL
- elif self.find('arrange_check_in') or self.find('arrange_check_in_on') is not None:
- self.scene = Scene.INFRA_DETAILS
- elif self.find('infra_overview_in') is not None:
+ elif self.find("infra_overview_in"):
self.scene = Scene.INFRA_ARRANGE
- elif self.find('arrange_confirm') is not None:
+ elif self.find("arrange_confirm"):
self.scene = Scene.INFRA_ARRANGE_CONFIRM
- elif self.find('friend_list') is not None:
- self.scene = Scene.FRIEND_LIST_OFF
- elif self.find("mission_trainee_on") is not None:
- self.scene = Scene.MISSION_TRAINEE
- elif self.find('mission_daily_on') is not None:
- self.scene = Scene.MISSION_DAILY
- elif self.find('mission_weekly_on') is not None:
- self.scene = Scene.MISSION_WEEKLY
- elif self.find('terminal_pre') is not None:
+ elif self.find("terminal_main"):
self.scene = Scene.TERMINAL_MAIN
- elif self.find('open_recruitment') is not None:
+ elif self.find("open_recruitment"):
self.scene = Scene.RECRUIT_MAIN
- elif self.find('recruiting_instructions') is not None:
+ elif self.find("recruiting_instructions"):
self.scene = Scene.RECRUIT_TAGS
- elif self.find('agent_token') is not None:
- self.scene = Scene.RECRUIT_AGENT
- elif self.find('agent_token_1080_1440') is not None:
- self.scene = Scene.RECRUIT_AGENT
- elif self.find('agent_token_900_1440') is not None:
- self.scene = Scene.RECRUIT_AGENT
- elif self.find('agent_unlock') is not None:
- self.scene = Scene.SHOP_CREDIT
- elif self.find('shop_credit_2') is not None:
+ elif self.find("credit_shop_countdown"):
+ hsv = cv2.cvtColor(self.img, cv2.COLOR_RGB2HSV)
+ if 9 < hsv[870][1530][0] < 19:
+ self.scene = Scene.UNKNOWN
+ else:
+ self.scene = Scene.SHOP_CREDIT
+ elif self.find("shop_credit_2"):
self.scene = Scene.SHOP_OTHERS
- elif self.find('shop_cart') is not None:
+ elif self.find("shop_cart"):
self.scene = Scene.SHOP_CREDIT_CONFIRM
- elif self.find('shop_assist') is not None:
- self.scene = Scene.SHOP_ASSIST
- elif self.find('login_logo') is not None and self.find('hypergryph') is not None:
- if self.find('login_awake') is not None:
+ elif self.find("login_logo") and self.find("hypergryph"):
+ if self.find("login_awake"):
self.scene = Scene.LOGIN_QUICKLY
- elif self.find('login_account') is not None:
+ elif self.find("login_account"):
self.scene = Scene.LOGIN_MAIN
- elif self.find('login_iknow') is not None:
- self.scene = Scene.LOGIN_ANNOUNCE
else:
self.scene = Scene.LOGIN_MAIN_NOENTRY
- elif self.find('register') is not None:
- self.scene = Scene.LOGIN_REGISTER
- elif self.find('login_loading') is not None:
+ elif self.find("login_loading"):
self.scene = Scene.LOGIN_LOADING
- elif self.find('login_iknow') is not None:
- self.scene = Scene.LOGIN_ANNOUNCE
- elif self.find('12cadpa') is not None:
- if self.find('cadpa_detail') is not None:
- self.scene = Scene.LOGIN_CADPA_DETAIL
- else:
- self.scene = Scene.LOGIN_START
- elif detector.announcement_close(self.img) is not None:
- self.scene = Scene.ANNOUNCEMENT
- elif self.find('skip') is not None:
+ elif self.find("12cadpa"):
+ self.scene = Scene.LOGIN_START
+ elif self.find("skip"):
self.scene = Scene.SKIP
- elif self.find('upgrade') is not None:
- self.scene = Scene.UPGRADE
- elif detector.confirm(self.img) is not None:
- self.scene = Scene.CONFIRM
- elif self.find('login_verify') is not None:
- self.scene = Scene.LOGIN_INPUT
- elif self.find('login_captcha') is not None:
- self.scene = Scene.LOGIN_CAPTCHA
- elif self.find('login_connecting') is not None:
+ elif self.find("login_connecting"):
self.scene = Scene.LOGIN_LOADING
- elif self.find('main_theme') is not None:
+ elif self.find("arrange_order_options"):
+ self.scene = Scene.RIIC_OPERATOR_SELECT
+ elif self.find("arrange_order_options_scene"):
+ self.scene = Scene.INFRA_ARRANGE_ORDER
+ elif self.find("ope_recover_potion_on"):
+ self.scene = Scene.OPERATOR_RECOVER_POTION
+ elif self.find("ope_recover_originite_on", scope=((1530, 120), (1850, 190))):
+ self.scene = Scene.OPERATOR_RECOVER_ORIGINITE
+ elif self.find("double_confirm/main"):
+ if self.find("double_confirm/exit"):
+ self.scene = Scene.EXIT_GAME
+ elif self.find("double_confirm/friend"):
+ self.scene = Scene.BACK_TO_FRIEND_LIST
+ elif self.find("double_confirm/give_up"):
+ self.scene = Scene.OPERATOR_GIVEUP
+ elif self.find("double_confirm/infrastructure"):
+ self.scene = Scene.LEAVE_INFRASTRUCTURE
+ elif self.find("double_confirm/recruit"):
+ self.scene = Scene.REFRESH_TAGS
+ elif self.find("double_confirm/network"):
+ self.scene = Scene.NETWORK_CHECK
+ elif self.find("double_confirm/voice"):
+ self.scene = Scene.DOWNLOAD_VOICE_RESOURCES
+ else:
+ self.scene = Scene.DOUBLE_CONFIRM
+ elif self.find("mission_trainee_on"):
+ self.scene = Scene.MISSION_TRAINEE
+ elif self.find("spent_credit"):
+ self.scene = Scene.SHOP_UNLOCK_SCHEDULE
+ elif self.find("loading7"):
+ self.scene = Scene.LOADING
+ elif self.find("clue/daily"):
+ self.scene = Scene.CLUE_DAILY
+ elif self.find("clue/receive"):
+ self.scene = Scene.CLUE_RECEIVE
+ elif self.find("clue/give_away"):
+ self.scene = Scene.CLUE_GIVE_AWAY
+ elif self.find("clue/summary"):
+ self.scene = Scene.CLUE_SUMMARY
+ elif self.find("clue/filter_all"):
+ self.scene = Scene.CLUE_PLACE
+ elif self.find("upgrade"):
+ self.scene = Scene.UPGRADE
+ elif self.find("depot"):
+ self.scene = Scene.DEPOT
+ elif self.find("pull_once"):
+ self.scene = Scene.HEADHUNTING
+ elif self.find("read_and_agree") or self.find("next_step"):
+ self.scene = Scene.AGREEMENT_UPDATE
+ elif self.is_black():
+ self.scene = Scene.LOADING
+
+ # 模板匹配
+ elif self.detect_index_scene():
+ self.scene = Scene.INDEX
+ elif self.find("materiel_ico"):
+ self.scene = Scene.MATERIEL
+ elif self.find("loading"):
+ self.scene = Scene.LOADING
+ elif self.find("loading2"):
+ self.scene = Scene.LOADING
+ elif self.find("loading3"):
+ self.scene = Scene.LOADING
+ elif self.find("loading4"):
+ self.scene = Scene.LOADING
+ elif self.find("ope_plan"):
+ self.scene = Scene.OPERATOR_BEFORE
+ elif self.find("navigation/episode"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/AP-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/LS-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/CA-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/CE-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/SK-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/PR-A-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/PR-B-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/PR-C-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("navigation/collection/PR-D-1"):
+ self.scene = Scene.OPERATOR_CHOOSE_LEVEL
+ elif self.find("ope_agency_going"):
+ self.scene = Scene.OPERATOR_ONGOING
+ elif self.find("ope_finish"):
+ self.scene = Scene.OPERATOR_FINISH
+ elif self.find("fight/use"):
+ self.scene = Scene.OPERATOR_STRANGER_SUPPORT
+ elif self.find("business_card"):
+ self.scene = Scene.BUSINESS_CARD
+ elif self.find("friend_list"):
+ self.scene = Scene.FRIEND_LIST
+ elif self.find("credit_visiting"):
+ self.scene = Scene.FRIEND_VISITING
+ elif self.find("arrange_check_in") or self.find("arrange_check_in_on"):
+ self.scene = Scene.INFRA_DETAILS
+ elif self.find("ope_failed"):
+ self.scene = Scene.OPERATOR_FAILED
+ elif self.find("mission_daily_on"):
+ self.scene = Scene.MISSION_DAILY
+ elif self.find("mission_weekly_on"):
+ self.scene = Scene.MISSION_WEEKLY
+ elif self.find("recruit/agent_token") or self.find("recruit/agent_token_first"):
+ self.scene = Scene.RECRUIT_AGENT
+ elif self.find("main_theme"):
self.scene = Scene.TERMINAL_MAIN_THEME
- elif self.find('episode') is not None:
+ elif self.find("episode"):
self.scene = Scene.TERMINAL_EPISODE
- elif self.find('biography') is not None:
+ elif self.find("biography"):
self.scene = Scene.TERMINAL_BIOGRAPHY
- elif self.find('collection') is not None:
+ elif self.find("collection"):
self.scene = Scene.TERMINAL_COLLECTION
- elif self.find('login_bilibili') is not None:
+ elif self.check_announcement():
+ self.scene = Scene.ANNOUNCEMENT
+
+ # 特征匹配
+ # elif self.find("login_new"):
+ # self.scene = Scene.LOGIN_NEW
+ elif self.find("login_bilibili"):
self.scene = Scene.LOGIN_BILIBILI
- elif self.find('loading6') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading7') is not None:
- self.scene = Scene.LOADING
- elif self.find('arrange_order_options_scene') is not None:
- self.scene = Scene.INFRA_ARRANGE_ORDER
+ elif self.find("login_bilibili_privacy"):
+ self.scene = Scene.LOGIN_BILIBILI_PRIVACY
+ elif self.find("login_captcha"):
+ self.scene = Scene.LOGIN_CAPTCHA
+
+ # 没弄完的
+ # elif self.find("ope_elimi_finished"):
+ # self.scene = Scene.OPERATOR_ELIMINATE_FINISH
+ # elif self.find("shop_assist"):
+ # self.scene = Scene.SHOP_ASSIST
+
else:
self.scene = Scene.UNKNOWN
- self.device.check_current_focus()
- # save screencap to analyse
- if config.SCREENSHOT_PATH is not None:
- self.save_screencap(self.scene)
- logger.info(f'Scene: {self.scene}: {SceneComment[self.scene]}')
+ self.check_current_focus()
+
+ logger.info(f"Scene {self.scene}: {SceneComment[self.scene]}")
+
return self.scene
- def get_infra_scene(self)-> int:
+ def find_ra_battle_exit(self) -> bool:
+ im = cv2.cvtColor(self.img, cv2.COLOR_RGB2HSV)
+ im = cv2.inRange(im, (29, 0, 0), (31, 255, 255))
+ score, scope = self.template_match(
+ "ra/battle_exit", ((75, 47), (165, 126)), cv2.TM_CCOEFF_NORMED
+ )
+ return scope if score > 0.8 else None
+
+ def detect_ra_adventure(self) -> bool:
+ img = cropimg(self.gray, ((385, 365), (475, 465)))
+ img = thres2(img, 250)
+ res = loadres("ra/adventure", True)
+ result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ logger.debug(f"{max_val=} {max_loc=}")
+ return max_val >= 0.9
+
+ def get_ra_scene(self) -> int:
+ """
+ 生息演算场景识别
+ """
+ # 场景缓存
if self.scene != Scene.UNDEFINED:
return self.scene
- if self.find('connecting', scope=((self.w//2, self.h//10*8), (self.w//4*3, self.h))) is not None:
+
+ # 连接中,优先级最高
+ if self.find("connecting"):
self.scene = Scene.CONNECTING
- elif self.find('double_confirm') is not None:
- if self.find('network_check') is not None:
- self.scene = Scene.NETWORK_CHECK
+ elif self.find("loading"):
+ self.scene = Scene.UNKNOWN
+ elif self.find("loading4"):
+ self.scene = Scene.UNKNOWN
+
+ # 奇遇
+ elif self.detect_ra_adventure():
+ self.scene = Scene.RA_ADVENTURE
+
+ # 快速跳过剧情对话
+ elif self.find("ra/guide_dialog"):
+ self.scene = Scene.RA_GUIDE_DIALOG
+
+ # 快速退出作战
+ elif self.find_ra_battle_exit():
+ self.scene = Scene.RA_BATTLE
+ elif self.find("ra/battle_exit_dialog"):
+ self.scene = Scene.RA_BATTLE_EXIT_CONFIRM
+
+ # 作战与分队
+ elif self.find("ra/squad_edit"):
+ self.scene = Scene.RA_SQUAD_EDIT
+ elif self.find("ra/start_action"):
+ if self.find("ra/action_points"):
+ self.scene = Scene.RA_BATTLE_ENTRANCE
else:
- self.scene = Scene.DOUBLE_CONFIRM
- elif self.find('infra_overview') is not None:
- self.scene = Scene.INFRA_MAIN
- elif self.find('infra_todo') is not None:
- self.scene = Scene.INFRA_TODOLIST
- elif self.find('clue') is not None:
- self.scene = Scene.INFRA_CONFIDENTIAL
- elif self.find('arrange_check_in') or self.find('arrange_check_in_on') is not None:
- self.scene = Scene.INFRA_DETAILS
- elif self.find('infra_overview_in') is not None:
- self.scene = Scene.INFRA_ARRANGE
- elif self.find('arrange_confirm') is not None:
- self.scene = Scene.INFRA_ARRANGE_CONFIRM
- elif self.find('arrange_order_options_scene') is not None:
- self.scene = Scene.INFRA_ARRANGE_ORDER
- elif self.find('loading') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading2') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading3') is not None:
- self.scene = Scene.LOADING
- elif self.find('loading4') is not None:
- self.scene = Scene.LOADING
- elif self.find('index_nav', thres=250, scope=((0, 0), (100+self.w//4, self.h//10))) is not None:
+ self.scene = Scene.RA_GUIDE_BATTLE_ENTRANCE
+ elif self.find("ra/get_item"):
+ self.scene = Scene.RA_GET_ITEM
+ elif self.find("ra/return_from_kitchen"):
+ self.scene = Scene.RA_KITCHEN
+ elif self.find("ra/squad_edit_confirm_dialog"):
+ self.scene = Scene.RA_SQUAD_EDIT_DIALOG
+ elif self.find("ra/enter_battle_confirm_dialog"):
+ self.scene = Scene.RA_SQUAD_ABNORMAL
+ elif self.find("ra/battle_complete"):
+ self.scene = Scene.RA_BATTLE_COMPLETE
+
+ # 结算界面
+ elif self.find("ra/day_complete"):
+ self.scene = Scene.RA_DAY_COMPLETE
+ elif self.find("ra/period_complete") and self.find("ra/click_anywhere"):
+ self.scene = Scene.RA_PERIOD_COMPLETE
+
+ # 森蚺图耶对话
+ elif self.find("ra/guide_entrance"):
+ self.scene = Scene.RA_GUIDE_ENTRANCE
+
+ # 存档操作
+ elif self.find("ra/delete_save_confirm_dialog"):
+ self.scene = Scene.RA_DELETE_SAVE_DIALOG
+
+ # 地图识别
+ elif self.find("ra/waste_time_button"):
+ self.scene = Scene.RA_DAY_DETAIL
+ elif self.find("ra/waste_time_dialog"):
+ self.scene = Scene.RA_WASTE_TIME_DIALOG
+ elif self.find("ra/map_back", thres=200) and self.color(1817, 333)[0] > 250:
+ self.scene = Scene.RA_MAP
+
+ # 一张便条
+ elif self.find("ra/notice"):
+ self.scene = Scene.RA_NOTICE
+
+ # 一张便条
+ elif self.find("ra/no_enough_drink"):
+ self.scene = Scene.RA_INSUFFICIENT_DRINK
+
+ # 从首页选择终端进入生息演算主页
+ elif self.find("terminal_longterm"):
+ self.scene = Scene.TERMINAL_LONGTERM
+ elif self.find("ra/main_title"):
+ self.scene = Scene.RA_MAIN
+ elif self.detect_index_scene():
self.scene = Scene.INDEX
- elif self.is_black():
- self.scene = Scene.LOADING
+ elif self.find("terminal_main"):
+ self.scene = Scene.TERMINAL_MAIN
+ else:
+ self.scene = Scene.UNKNOWN
+ self.check_current_focus()
+
+ log_msg = f"Scene: {self.scene}: {SceneComment[self.scene]}"
+ if self.scene == Scene.UNKNOWN:
+ logger.debug(log_msg)
+ else:
+ logger.info(log_msg)
+
+ self.check_loading_time()
+
+ return self.scene
+
+ def get_sf_scene(self) -> int:
+ """
+ 隐秘战线场景识别
+ """
+ # 场景缓存
+ if self.scene != Scene.UNDEFINED:
+ return self.scene
+
+ # 连接中,优先级最高
+ if self.find("connecting"):
+ self.scene = Scene.CONNECTING
+
+ elif self.find("sf/success") or self.find("sf/failure"):
+ self.scene = Scene.SF_RESULT
+ elif self.find("sf/continue"):
+ self.scene = Scene.SF_CONTINUE
+ elif self.find("sf/select"):
+ self.scene = Scene.SF_SELECT
+ elif self.find("sf/properties"):
+ self.scene = Scene.SF_ACTIONS
+ elif self.find("sf/continue_event"):
+ self.scene = Scene.SF_EVENT
+ elif self.find("sf/team_pass"):
+ self.scene = Scene.SF_TEAM_PASS
+
+ elif self.find("sf/inheritance", scope=((1490, 0), (1920, 100))):
+ self.scene = Scene.SF_SELECT_TEAM
+
+ # 从首页进入隐秘战线
+ elif self.detect_index_scene():
+ self.scene = Scene.INDEX
+ elif self.find("terminal_main"):
+ self.scene = Scene.TERMINAL_MAIN
+ elif self.find("main_theme"):
+ self.scene = Scene.TERMINAL_MAIN_THEME
+ elif self.find("sf/entrance"):
+ self.scene = Scene.SF_ENTRANCE
+ elif self.find("sf/info"):
+ self.scene = Scene.SF_INFO
+
+ elif self.find("sf/click_anywhere"):
+ self.scene = Scene.SF_CLICK_ANYWHERE
+ elif self.find("sf/end"):
+ self.scene = Scene.SF_END
+ elif self.find("sf/exit"):
+ self.scene = Scene.SF_EXIT
+
else:
self.scene = Scene.UNKNOWN
- self.device.check_current_focus()
- # save screencap to analyse
- if config.SCREENSHOT_PATH is not None:
- self.save_screencap(self.scene)
- logger.info(f'Scene: {self.scene}: {SceneComment[self.scene]}')
+ self.check_current_focus()
+
+ log_msg = f"Scene: {self.scene}: {SceneComment[self.scene]}"
+ if self.scene == Scene.UNKNOWN:
+ logger.debug(log_msg)
+ else:
+ logger.info(log_msg)
+
+ self.check_loading_time()
+
+ return self.scene
+
+ def get_sss_scene(self) -> int:
+ """
+ 保全导航场景识别
+ """
+ # 场景缓存
+ if self.scene != Scene.UNDEFINED:
+ return self.scene
+
+ # 连接中,优先级最高
+ if self.find("connecting"):
+ self.scene = Scene.CONNECTING
+
+ elif self.detect_index_scene():
+ self.scene = Scene.INDEX
+ elif self.find("terminal_main") is not None:
+ self.scene = Scene.TERMINAL_MAIN
+ elif self.find("terminal_regular"):
+ self.scene = Scene.TERMINAL_REGULAR
+ elif self.find("sss/main"):
+ self.scene = Scene.SSS_MAIN
+ elif self.find("sss/start_button"):
+ self.scene = Scene.SSS_START
+ elif self.find("sss/ec_button"):
+ self.scene = Scene.SSS_EC
+ elif self.find("sss/device_button"):
+ self.scene = Scene.SSS_DEVICE
+ elif self.find("sss/squad_button"):
+ self.scene = Scene.SSS_SQUAD
+ elif self.find("sss/deploy_button"):
+ self.scene = Scene.SSS_DEPLOY
+ elif self.find("sss/redeploy_button"):
+ self.scene = Scene.SSS_REDEPLOY
+ elif self.find("sss/loading"):
+ self.scene = Scene.SSS_LOADING
+ elif self.find("sss/close_button"):
+ self.scene = Scene.SSS_GUIDE
+ else:
+ self.scene = Scene.UNKNOWN
+ self.check_current_focus()
+
+ logger.info(f"Scene: {self.scene}: {SceneComment[self.scene]}")
+
+ self.check_loading_time()
+
+ return self.scene
+
+ def get_train_scene(self) -> int:
+ """
+ 训练室场景识别
+ """
+ # 场景缓存
+ if self.scene != Scene.UNDEFINED:
+ return self.scene
+ # 连接中,优先级最高
+ if self.find("connecting"):
+ self.scene = Scene.CONNECTING
+ elif self.find("infra_overview"):
+ self.scene = Scene.INFRA_MAIN
+ elif self.find("train_main"):
+ self.scene = Scene.TRAIN_MAIN
+ elif self.find("skill_collect_confirm", scope=((1142, 831), (1282, 932))):
+ self.scene = Scene.TRAIN_FINISH
+ elif self.find("training_support"):
+ self.scene = Scene.TRAIN_SKILL_SELECT
+ elif self.find("upgrade_failure"):
+ self.scene = Scene.TRAIN_SKILL_UPGRADE_ERROR
+ elif self.find("skill_confirm"):
+ self.scene = Scene.TRAIN_SKILL_UPGRADE
+ else:
+ self.scene = Scene.UNKNOWN
+ self.check_current_focus()
+
+ logger.info(f"Scene: {self.scene}: {SceneComment[self.scene]}")
+
+ self.check_loading_time()
+
return self.scene
def is_black(self) -> None:
- """ check if the current scene is all black """
+ """check if the current scene is all black"""
return np.max(self.gray[:, 105:-105]) < 16
- def nav_button(self):
- """ find navigation button """
- return self.find('nav_button', thres=128, scope=((0, 0), (100+self.w//4, self.h//10)))
-
- def find(self, res: str, draw: bool = False, scope: tp.Scope = None, thres: int = None, judge: bool = True, strict: bool = False) -> tp.Scope:
+ def find(
+ self,
+ res: tp.Res,
+ draw: bool = False,
+ scope: tp.Scope | None = None,
+ thres: int | None = None,
+ judge: bool = True,
+ strict: bool = False,
+ threshold: float = 0.0,
+ ) -> tp.Scope:
"""
查找元素是否出现在画面中
@@ -280,27 +638,276 @@ def find(self, res: str, draw: bool = False, scope: tp.Scope = None, thres: int
:param thres: 是否在匹配前对图像进行二值化处理
:param judge: 是否加入更加精确的判断
:param strict: 是否启用严格模式,未找到时报错
+ :param score: 是否启用分数限制,有些图片精确识别需要提高分数阈值
:return ret: 若匹配成功,则返回元素在游戏界面中出现的位置,否则返回 None
"""
- logger.debug(f'find: {res}')
- res = f'{__rootdir__}/resources/{res}.png'
+ logger.debug(f"find: {res}")
+
+ color = {
+ "1800": (158, 958),
+ "12cadpa": (1810, 21),
+ "arrange_confirm": (755, 903),
+ "arrange_order_options": (1652, 23),
+ "arrange_order_options_scene": (369, 199),
+ "clue": (1740, 855),
+ "clue/daily": (526, 623),
+ "clue/filter_all": (1297, 99),
+ "clue/give_away": (25, 18),
+ "clue/receive": (1295, 15),
+ "clue/summary": (59, 153),
+ "confirm": (0, 683),
+ "control_central_assistants": (39, 560),
+ "credit_shop_countdown": (1511, 1017),
+ "depot": (0, 955),
+ "double_confirm/exit": (940, 464),
+ "double_confirm/friend": (978, 465),
+ "double_confirm/give_up": (574, 716),
+ "double_confirm/infrastructure": (1077, 435),
+ "double_confirm/main": (835, 683),
+ "double_confirm/network": (708, 435),
+ "double_confirm/recruit": (981, 464),
+ "double_confirm/voice": (745, 435),
+ "drone": (274, 437),
+ "factory_collect": (1542, 886),
+ "fight/refresh": (1639, 22),
+ "hypergryph": (0, 961),
+ "infra_overview": (54, 135),
+ "infra_overview_in": (64, 705),
+ "infra_todo": (13, 1013),
+ "loading2": (620, 247),
+ "loading7": (106, 635),
+ "login_account": (622, 703),
+ "login_awake": (888, 743),
+ "login_connecting": (760, 881),
+ "login_loading": (920, 388),
+ "login_logo": (601, 332),
+ "mail": (307, 39),
+ "mission_trainee_on": (690, 17),
+ "nav_bar": (655, 0),
+ "nav_button": (26, 20),
+ "navigation/collection/AP-1": (203, 821),
+ "navigation/collection/CA-1": (203, 821),
+ "navigation/collection/CE-1": (243, 822),
+ "navigation/collection/LS-1": (240, 822),
+ "navigation/collection/SK-1": (204, 821),
+ "navigation/collection/PR-A-1": (550, 629),
+ "navigation/collection/PR-B-1": (496, 629),
+ "navigation/collection/PR-C-1": (487, 586),
+ "navigation/collection/PR-D-1": (516, 619),
+ "navigation/ope_hard_small": (819, 937),
+ "navigation/ope_normal_small": (494, 930),
+ "navigation/record_restoration": (274, 970),
+ "next_step": (915, 811),
+ "ope_agency_lock": [(1565, 856), (1565, 875)],
+ "ope_elimi_agency_confirm": (1554, 941),
+ "ope_elimi_agency_panel": (1409, 612),
+ "ope_eliminate": (1332, 938),
+ "ope_recover_originite_on": (1514, 124),
+ "ope_recover_potion_on": (1046, 127),
+ "ope_select_start": (1579, 701),
+ "open_recruitment": (192, 143),
+ "order_label": (404, 137),
+ "pull_once": (1260, 950),
+ "read_and_agree": (1115, 767),
+ "recruiting_instructions": (343, 179),
+ "riic/exp": (1385, 239),
+ "riic/manufacture": (1328, 126),
+ "riic/report_title": (1712, 25),
+ "spent_credit": (332, 264),
+ "shop_cart": (1252, 842),
+ "shop_credit_2": (1657, 135),
+ "skip": (1803, 32),
+ "terminal_main": (73, 959),
+ "terminal_pre2": (1459, 797),
+ }
+ if res in color:
+ res_img = loadres(res)
+ h, w, _ = res_img.shape
+
+ pos_list = color[res]
+ if not isinstance(pos_list[0], tuple):
+ pos_list = [color[res]]
+ for pos in pos_list:
+ scope = pos, va(pos, (w, h))
+ img = cropimg(self.img, scope)
+ if cmatch(img, res_img, draw=draw):
+ gray = cropimg(self.gray, scope)
+ res_img = cv2.cvtColor(res_img, cv2.COLOR_RGB2GRAY)
+ ssim = structural_similarity(gray, res_img)
+ logger.debug(f"{ssim=}")
+ if ssim >= 0.9:
+ return scope
+
+ return None
+
+ template_matching = {
+ "arrange_check_in": ((30, 300), (175, 700)),
+ "arrange_check_in_on": ((30, 300), (175, 700)),
+ "biography": (768, 934),
+ "business_card": (55, 165),
+ "collection": (1005, 943),
+ "collection_small": (1053, 982),
+ "connecting": (1087, 978),
+ "episode": (535, 937),
+ "fight/use": (858, 864),
+ "friend_list": (61, 306),
+ "credit_visiting": (78, 220),
+ "loading": (736, 333),
+ "loading2": (620, 247),
+ "loading3": (1681, 1000),
+ "loading4": (828, 429),
+ "main_theme": (283, 945),
+ "main_theme_small": (321, 973),
+ "materiel_ico": (892, 61),
+ "mission_daily_on": ((685, 15), (1910, 100)),
+ "mission_weekly_on": ((685, 15), (1910, 100)),
+ "navigation/collection/AP_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/CA_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/CE_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/LS_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/SK_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/PR-A_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/PR-B_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/PR-C_entry": ((0, 170), (1920, 870)),
+ "navigation/collection/PR-D_entry": ((0, 170), (1920, 870)),
+ "navigation/episode": (1560, 944),
+ "navigation/ope_difficulty": [(0, 920), (120, 1080)],
+ "navigation/ope_normal": (172, 950),
+ "navigation/ope_normal_small": (494, 930),
+ "navigation/ope_hard": (172, 950),
+ "navigation/ope_hard_small": (819, 937),
+ "ope_agency_going": ((508, 941), (715, 1021)),
+ "ope_agency_fail": (809, 959),
+ "ope_failed": (183, 465),
+ "ope_finish": (87, 265),
+ "ope_plan": (1278, 24),
+ "ope_select_start_empty": ((0, 0), (400, 400)),
+ "recruit/agent_token": ((1740, 765), (1920, 805)),
+ "recruit/agent_token_first": ((1700, 760), (1920, 810)),
+ "recruit/available_level": (1294, 234),
+ "recruit/begin_recruit": scope,
+ "recruit/career_needs": (350, 593),
+ "recruit/lmb": (945, 27),
+ "recruit/recruit_done": scope,
+ "recruit/recruit_lock": scope,
+ "recruit/job_requirements": scope,
+ "recruit/ticket": ((900, 0), (1920, 120)),
+ "recruit/time": (1304, 112),
+ "recruit/refresh": (1366, 560),
+ "recruit/refresh_comfirm": (1237, 714),
+ "recruit/riic_res/CASTER": ((750, 730), (1920, 860)),
+ "recruit/riic_res/MEDIC": ((750, 730), (1920, 860)),
+ "recruit/riic_res/PIONEER": ((750, 730), (1920, 860)),
+ "recruit/riic_res/SPECIAL": ((750, 730), (1920, 860)),
+ "recruit/riic_res/SNIPER": ((750, 730), (1920, 860)),
+ "recruit/riic_res/SUPPORT": ((750, 730), (1920, 860)),
+ "recruit/riic_res/TANK": ((750, 730), (1920, 860)),
+ "recruit/riic_res/WARRIOR": ((750, 730), (1920, 860)),
+ "recruit/start_recruit": (1438, 849),
+ "recruit/stone": ((900, 0), (1920, 120)),
+ "riic/assistants": ((1320, 400), (1600, 650)),
+ "riic/iron": ((1570, 230), (1630, 340)),
+ "riic/orundum": ((1500, 320), (1800, 550)),
+ "riic/trade": ((1320, 250), (1600, 500)),
+ "upgrade": (997, 501),
+ }
+
+ template_matching_score = {
+ "connecting": 0.7,
+ "navigation/ope_hard": 0.7,
+ "navigation/ope_hard_small": 0.7,
+ "navigation/ope_normal": 0.7,
+ "navigation/ope_normal_small": 0.7,
+ "recruit/agent_token": 0.8,
+ "recruit/agent_token_first": 0.8,
+ "recruit/lmb": 0.7,
+ "recruit/riic_res/CASTER": 0.7,
+ "recruit/riic_res/MEDIC": 0.7,
+ "recruit/riic_res/PIONEER": 0.7,
+ "recruit/riic_res/SPECIAL": 0.7,
+ "recruit/riic_res/SNIPER": 0.7,
+ "recruit/riic_res/SUPPORT": 0.7,
+ "recruit/riic_res/TANK": 0.7,
+ "recruit/riic_res/WARRIOR": 0.7,
+ "recruit/time": 0.8,
+ "recruit/stone": 0.7,
+ }
+
+ if res in template_matching:
+ threshold = 0.9
+ if res in template_matching_score:
+ threshold = template_matching_score[res]
+
+ pos = template_matching[res]
+ res = loadres(res, True)
+ h, w = res.shape
+
+ if isinstance(pos[0], tuple):
+ scope = pos
+ else:
+ scope = pos, va(pos, (w, h))
+
+ img = cropimg(self.gray, scope)
+ result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ top_left = va(max_loc, scope[0])
+ logger.debug(f"{top_left=} {max_val=}")
+ if max_val >= threshold:
+ return top_left, va(top_left, (w, h))
+ return None
+
+ dpi_aware = res in [
+ "login_bilibili",
+ "login_bilibili_privacy",
+ "login_bilibili_entry",
+ "login_bilibili_privacy_accept",
+ "login_captcha",
+ "control_central",
+ ]
+
+ if scope is None and threshold == 0.0:
+ if res == "arrange_check_in":
+ scope = ((0, 350), (200, 530))
+ threshold = 0.55
+ elif res == "arrange_check_in_on":
+ scope = ((0, 350), (200, 530))
+ elif res == "connecting":
+ scope = ((1087, 978), (1430, 1017))
+ threshold = 0.15
+ elif res == "materiel_ico":
+ scope = ((860, 60), (1072, 217))
+ elif res == "training_completed":
+ scope = ((550, 900), (800, 1080))
+ threshold = 0.45
+
+ res_img = loadres(res, True)
if thres is not None:
# 对图像二值化处理
- res_img = thres2(loadimg(res, True), thres)
- gray_img = cropimg(self.gray, scope)
- matcher = Matcher(thres2(gray_img, thres))
- ret = matcher.match(res_img, draw=draw, judge=judge)
+ res_img = thres2(res_img, thres)
+ matcher = Matcher(thres2(self.gray, thres))
else:
- res_img = loadimg(res, True)
matcher = self.matcher
- ret = matcher.match(res_img, draw=draw, scope=scope, judge=judge)
+ ret = matcher.match(
+ res_img,
+ draw=draw,
+ scope=scope,
+ judge=judge,
+ prescore=threshold,
+ dpi_aware=dpi_aware,
+ )
if strict and ret is None:
- raise RecognizeError(f"Can't find '{res}'")
+ raise RecognizeError(f"Can't find '{res}'")
return ret
- def score(self, res: str, draw: bool = False, scope: tp.Scope = None, thres: int = None) -> Optional[List[float]]:
+ def score(
+ self,
+ res: str,
+ draw: bool = False,
+ scope: tp.Scope = None,
+ thres: int | None = None,
+ ) -> Optional[List[float]]:
"""
查找元素是否出现在画面中,并返回分数
@@ -311,17 +918,52 @@ def score(self, res: str, draw: bool = False, scope: tp.Scope = None, thres: int
:return ret: 若匹配成功,则返回元素在游戏界面中出现的位置,否则返回 None
"""
- logger.debug(f'find: {res}')
- res = f'{__rootdir__}/resources/{res}.png'
+ logger.debug(f"find: {res}")
+ res = f"{__rootdir__}/resources/{res}.png"
+ res_img = loadres(res, True)
if thres is not None:
# 对图像二值化处理
- res_img = thres2(loadimg(res, True), thres)
+ res_img = thres2(res_img, thres)
gray_img = cropimg(self.gray, scope)
matcher = Matcher(thres2(gray_img, thres))
- score = matcher.score(res_img, draw=draw, only_score=True)
else:
- res_img = loadimg(res, True)
matcher = self.matcher
- score = matcher.score(res_img, draw=draw, scope=scope, only_score=True)
+ score = matcher.score(res_img, draw=draw, scope=scope, only_score=True)
return score
+
+ def template_match(
+ self,
+ res: str,
+ scope: Optional[tp.Scope] = None,
+ method: int = cv2.TM_CCOEFF_NORMED,
+ ) -> Tuple[float, tp.Scope]:
+ logger.debug(f"template_match: {res}")
+
+ template = loadres(res, True)
+ w, h = template.shape[::-1]
+
+ if scope:
+ x, y = scope[0]
+ img = cropimg(self.gray, scope)
+ else:
+ x, y = (0, 0)
+ img = self.gray
+
+ result = cv2.matchTemplate(img, template, method)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+
+ if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
+ top_left = min_loc
+ score = min_val
+ else:
+ top_left = max_loc
+ score = max_val
+
+ p1 = (top_left[0] + x, top_left[1] + y)
+ p2 = (p1[0] + w, p1[1] + h)
+
+ ret_val = (score, (p1, p2))
+ logger.debug(f"template_match: {ret_val}")
+
+ return ret_val
diff --git a/arknights_mower/utils/scene.py b/arknights_mower/utils/scene.py
index b152b4355..e999e84a7 100644
--- a/arknights_mower/utils/scene.py
+++ b/arknights_mower/utils/scene.py
@@ -1,16 +1,425 @@
-from ..data import scene_list
-
-
class Scene:
- pass
-
-
-SceneComment = {}
+ UNKNOWN = -1
+ "未知"
+ UNDEFINED = 0
+ "未定义"
+ INDEX = 1
+ "首页"
+ MATERIEL = 2
+ "物资领取确认"
+ ANNOUNCEMENT = 3
+ "公告"
+ MAIL = 4
+ "邮件信箱"
+ NAVIGATION_BAR = 5
+ "导航栏返回"
+ UPGRADE = 6
+ "升级"
+ SKIP = 7
+ "跳过"
+ DOUBLE_CONFIRM = 8
+ "二次确认(未知)"
+ CONNECTING = 9
+ "正在提交反馈至神经"
+ NETWORK_CHECK = 10
+ "网络拨测"
+ EXIT_GAME = 11
+ "退出游戏"
+ DOWNLOAD_VOICE_RESOURCES = 12
+ "检测到有未下载的语音资源"
+ AGREEMENT_UPDATE = 13
+ "协议更新"
+ LOGIN_MAIN = 101
+ "登录页面"
+ LOGIN_INPUT = 102
+ "登录页面(输入)"
+ LOGIN_QUICKLY = 103
+ "登录页面(快速)"
+ LOGIN_LOADING = 104
+ "登录中"
+ LOGIN_START = 105
+ "启动"
+ LOGIN_ANNOUNCE = 106
+ "启动界面公告"
+ LOGIN_REGISTER = 107
+ "注册"
+ LOGIN_CAPTCHA = 108
+ "滑动验证码"
+ LOGIN_BILIBILI = 109
+ "B 服登录界面"
+ LOGIN_MAIN_NOENTRY = 110
+ "登录页面(无按钮入口)"
+ LOGIN_CADPA_DETAIL = 111
+ "游戏适龄提示"
+ CLOSE_MINE = 112
+ "产业合作洽谈会"
+ CHECK_IN = 113
+ "4周年签到"
+ LOGIN_NEW = 114
+ "新登陆界面"
+ LOGIN_BILIBILI_PRIVACY = 116
+ "B服隐私政策提示"
+ INFRA_MAIN = 201
+ "基建全局视角"
+ INFRA_TODOLIST = 202
+ "基建待办事项"
+ INFRA_CONFIDENTIAL = 203
+ "线索主界面"
+ INFRA_ARRANGE = 204
+ "基建干员进驻总览"
+ INFRA_DETAILS = 205
+ "基建放大查看"
+ INFRA_ARRANGE_CONFIRM = 206
+ "基建干员排班二次确认"
+ INFRA_ARRANGE_ORDER = 207
+ "干员进驻设施排序界面"
+ RIIC_REPORT = 208
+ "副手简报界面"
+ CTRLCENTER_ASSISTANT = 209
+ "控制中枢界面"
+ RIIC_OPERATOR_SELECT = 210
+ "干员选择界面"
+ CLUE_DAILY = 211
+ "每日线索领取"
+ CLUE_RECEIVE = 212
+ "接收线索"
+ CLUE_GIVE_AWAY = 213
+ "传递线索"
+ CLUE_SUMMARY = 214
+ "线索交流活动汇总"
+ CLUE_PLACE = 215
+ "放置线索"
+ TRAIN_SKILL_UPGRADE = 216
+ "技能升级"
+ TRAIN_MAIN = 217
+ "训练室主界面"
+ TRAIN_SKILL_UPGRADE_ERROR = 218
+ "技能升级失败"
+ TRAIN_SKILL_SELECT = 219
+ "选择技能"
+ TRAIN_FINISH = 220
+ "技能升级结算"
+ ORDER_LIST = 221
+ "贸易站订单列表"
+ DRONE_ACCELERATE = 222
+ "无人机加速对话框"
+ FACTORY_ROOMS = 223
+ "制造站设施列表"
+ LEAVE_INFRASTRUCTURE = 224
+ "离开基建"
+ BUSINESS_CARD = 301
+ "个人名片"
+ FRIEND_LIST = 302
+ "好友列表"
+ FRIEND_VISITING = 303
+ "基建内访问好友"
+ BACK_TO_FRIEND_LIST = 304
+ "返回好友列表"
+ MISSION_DAILY = 401
+ "日常任务"
+ MISSION_WEEKLY = 402
+ "周常任务"
+ MISSION_TRAINEE = 403
+ "见习任务"
+ TERMINAL_MAIN = 501
+ "终端主界面"
+ TERMINAL_MAIN_THEME = 502
+ "主题曲"
+ TERMINAL_EPISODE = 503
+ "插曲"
+ TERMINAL_BIOGRAPHY = 504
+ "别传"
+ TERMINAL_COLLECTION = 505
+ "资源收集"
+ TERMINAL_REGULAR = 506
+ "常态事务"
+ TERMINAL_LONGTERM = 507
+ "长期探索"
+ TERMINAL_PERIODIC = 508
+ "周期挑战"
+ OPERATOR_CHOOSE_LEVEL = 601
+ "作战前,关卡未选定"
+ OPERATOR_BEFORE = 602
+ "作战前,关卡已选定"
+ OPERATOR_SELECT = 603
+ "作战前,正在编队"
+ OPERATOR_ONGOING = 604
+ "代理作战"
+ OPERATOR_FINISH = 605
+ "作战结束"
+ OPERATOR_RECOVER_POTION = 607
+ "恢复理智(药剂)"
+ OPERATOR_RECOVER_ORIGINITE = 608
+ "恢复理智(源石)"
+ OPERATOR_DROP = 609
+ "掉落物品详细说明页"
+ OPERATOR_ELIMINATE = 610
+ "剿灭作战前,关卡已选定"
+ OPERATOR_ELIMINATE_FINISH = 611
+ "剿灭作战结束"
+ OPERATOR_GIVEUP = 612
+ "放弃行动"
+ OPERATOR_FAILED = 613
+ "代理作战失败"
+ OPERATOR_ELIMINATE_AGENCY = 614
+ "剿灭代理卡使用确认"
+ OPERATOR_SUPPORT = 615
+ "借助战"
+ OPERATOR_STRANGER_SUPPORT = 616
+ "使用非好友助战"
+ SHOP_OTHERS = 701
+ "商店除了信用兑换处以外的界面"
+ SHOP_CREDIT = 702
+ "信用兑换处"
+ SHOP_CREDIT_CONFIRM = 703
+ "兑换确认"
+ SHOP_ASSIST = 704
+ "助战使用次数"
+ SHOP_UNLOCK_SCHEDULE = 705
+ "累计信用消费"
+ RECRUIT_MAIN = 801
+ "公招主界面"
+ RECRUIT_TAGS = 802
+ "挑选标签时"
+ RECRUIT_AGENT = 803
+ "开包干员展示"
+ REFRESH_TAGS = 804
+ "刷新词条"
+ RA_MAIN = 901
+ "生息演算首页"
+ RA_GUIDE_ENTRANCE = 902
+ "剧情入口:众人会聚之地(后舍)"
+ RA_GUIDE_DIALOG = 903
+ "剧情对话"
+ RA_BATTLE_ENTRANCE = 904
+ "作战入口"
+ RA_BATTLE = 905
+ "作战中"
+ RA_BATTLE_EXIT_CONFIRM = 906
+ "作战退出确认对话框"
+ RA_GUIDE_BATTLE_ENTRANCE = 907
+ "剧情作战入口"
+ RA_BATTLE_COMPLETE = 908
+ "作战结算"
+ RA_MAP = 909
+ "地图"
+ RA_SQUAD_EDIT = 910
+ "作战分队编辑"
+ RA_KITCHEN = 911
+ "烹饪台"
+ RA_GET_ITEM = 912
+ "获得物资"
+ RA_SQUAD_EDIT_DIALOG = 913
+ "作战分队不携带干员确认"
+ RA_DAY_COMPLETE = 914
+ "生息日结算"
+ RA_DAY_DETAIL = 915
+ "当日详细信息"
+ RA_WASTE_TIME_DIALOG = 916
+ "跳过行动确认对话框"
+ RA_PERIOD_COMPLETE = 917
+ "生存周期完成"
+ RA_DELETE_SAVE_DIALOG = 918
+ "存档删除确认"
+ RA_ADVENTURE = 919
+ "奇遇"
+ RA_NOTICE = 920
+ "一张便条"
+ RA_INSUFFICIENT_DRINK = 921
+ "能量饮料不足"
+ RA_SQUAD_ABNORMAL = 922
+ "当前编队中存在异常情况"
+ SSS_MAIN = 1001
+ "保全作战首页"
+ SSS_START = 1002
+ "开始保全作战"
+ SSS_EC = 1003
+ "定向导能元件"
+ SSS_DEVICE = 1004
+ "战术装备配置"
+ SSS_SQUAD = 1005
+ "首批作战小队选任"
+ SSS_DEPLOY = 1006
+ "开始部署"
+ SSS_REDEPLOY = 1007
+ "重新部署"
+ SSS_LOADING = 1008
+ "正在进入"
+ SSS_GUIDE = 1009
+ "保全教程"
+ SF_ENTRANCE = 1101
+ "隐秘战线入口"
+ SF_INFO = 1102
+ "隐秘战线详细说明"
+ SF_SELECT_TEAM = 1103
+ "选择小队"
+ SF_CONTINUE = 1104
+ "继续前进"
+ SF_SELECT = 1105
+ "选择路线"
+ SF_ACTIONS = 1106
+ "行动选项"
+ SF_RESULT = 1107
+ "行动结果"
+ SF_EVENT = 1108
+ "应对危机事件"
+ SF_TEAM_PASS = 1109
+ "小队通过危机事件"
+ SF_CLICK_ANYWHERE = 1110
+ "点击任意处继续"
+ SF_END = 1111
+ "抵达终点"
+ SF_EXIT = 1112
+ "暂离行动"
+ HEADHUNTING = 1201
+ "干员寻访"
+ DEPOT = 1301
+ "仓库"
+ LOADING = 9998
+ "场景跳转时的等待界面"
+ CONFIRM = 9999
+ "确认对话框"
-for scene in scene_list.keys():
- id = int(scene)
- label = scene_list[scene]['label']
- comment = scene_list[scene]['comment']
- setattr(Scene, label, id)
- SceneComment[id] = comment
+SceneComment = {
+ -1: "未知",
+ 0: "未定义",
+ 1: "首页",
+ 2: "物资领取确认",
+ 3: "公告",
+ 4: "邮件信箱",
+ 5: "导航栏返回",
+ 6: "升级",
+ 7: "跳过",
+ 8: "二次确认(未知)",
+ 9: "正在提交反馈至神经",
+ 10: "网络拨测",
+ 11: "退出游戏",
+ 12: "检测到有未下载的语音资源",
+ 13: "协议更新",
+ 101: "登录页面",
+ 102: "登录页面(输入)",
+ 103: "登录页面(快速)",
+ 104: "登录中",
+ 105: "启动",
+ 106: "启动界面公告",
+ 107: "注册",
+ 108: "滑动验证码",
+ 109: "B 服登录界面",
+ 110: "登录页面(无按钮入口)",
+ 111: "游戏适龄提示",
+ 112: "产业合作洽谈会",
+ 113: "4周年签到",
+ 114: "新登陆界面",
+ 116: "B服隐私政策提示",
+ 201: "基建全局视角",
+ 202: "基建待办事项",
+ 203: "线索主界面",
+ 204: "基建干员进驻总览",
+ 205: "基建放大查看",
+ 206: "基建干员排班二次确认",
+ 207: "干员进驻设施排序界面",
+ 208: "副手简报界面",
+ 209: "控制中枢界面",
+ 210: "干员选择界面",
+ 211: "每日线索领取",
+ 212: "接收线索",
+ 213: "传递线索",
+ 214: "线索交流活动汇总",
+ 215: "放置线索",
+ 216: "技能升级",
+ 217: "训练室主界面",
+ 218: "技能升级失败",
+ 219: "选择技能",
+ 220: "技能升级结算",
+ 221: "贸易站订单列表",
+ 222: "无人机加速对话框",
+ 223: "制造站设施列表",
+ 224: "离开基建",
+ 301: "个人名片",
+ 302: "好友列表",
+ 303: "基建内访问好友",
+ 304: "返回好友列表",
+ 401: "日常任务",
+ 402: "周常任务",
+ 403: "见习任务",
+ 501: "终端主界面",
+ 502: "主题曲",
+ 503: "插曲",
+ 504: "别传",
+ 505: "资源收集",
+ 506: "常态事务",
+ 507: "长期探索",
+ 508: "周期挑战",
+ 601: "作战前,关卡未选定",
+ 602: "作战前,关卡已选定",
+ 603: "作战前,正在编队",
+ 604: "代理作战",
+ 605: "作战结束",
+ 607: "恢复理智(药剂)",
+ 608: "恢复理智(源石)",
+ 609: "掉落物品详细说明页",
+ 610: "剿灭作战前,关卡已选定",
+ 611: "剿灭作战结束",
+ 612: "放弃行动",
+ 613: "代理作战失败",
+ 614: "剿灭代理卡使用确认",
+ 615: "借助战",
+ 616: "使用非好友助战",
+ 701: "商店除了信用兑换处以外的界面",
+ 702: "信用兑换处",
+ 703: "兑换确认",
+ 704: "助战使用次数",
+ 705: "累计信用消费",
+ 801: "公招主界面",
+ 802: "挑选标签时",
+ 803: "开包干员展示",
+ 804: "刷新词条",
+ 901: "生息演算首页",
+ 902: "剧情入口:众人会聚之地(后舍)",
+ 903: "剧情对话",
+ 904: "作战入口",
+ 905: "作战中",
+ 906: "作战退出确认对话框",
+ 907: "剧情作战入口",
+ 908: "作战结算",
+ 909: "地图",
+ 910: "作战分队编辑",
+ 911: "烹饪台",
+ 912: "获得物资",
+ 913: "作战分队不携带干员确认",
+ 914: "生息日结算",
+ 915: "当日详细信息",
+ 916: "跳过行动确认对话框",
+ 917: "生存周期完成",
+ 918: "存档删除确认",
+ 919: "奇遇",
+ 920: "一张便条",
+ 921: "能量饮料不足",
+ 922: "当前编队中存在异常情况",
+ 1001: "保全作战首页",
+ 1002: "开始保全作战",
+ 1003: "定向导能元件",
+ 1004: "战术装备配置",
+ 1005: "首批作战小队选任",
+ 1006: "开始部署",
+ 1007: "重新部署",
+ 1008: "正在进入",
+ 1009: "保全教程",
+ 1101: "隐秘战线入口",
+ 1102: "隐秘战线详细说明",
+ 1103: "选择小队",
+ 1104: "继续前进",
+ 1105: "选择路线",
+ 1106: "行动选项",
+ 1107: "行动结果",
+ 1108: "应对危机事件",
+ 1109: "小队通过危机事件",
+ 1110: "点击任意处继续",
+ 1111: "抵达终点",
+ 1112: "暂离行动",
+ 1201: "干员寻访",
+ 1301: "仓库",
+ 9998: "场景跳转时的等待界面",
+ 9999: "确认对话框",
+}
diff --git a/arknights_mower/utils/scheduler_task.py b/arknights_mower/utils/scheduler_task.py
new file mode 100644
index 000000000..2b1aca2ae
--- /dev/null
+++ b/arknights_mower/utils/scheduler_task.py
@@ -0,0 +1,309 @@
+import copy
+from datetime import datetime, timedelta
+from enum import Enum
+from typing import Literal
+
+from arknights_mower.utils import config
+from arknights_mower.utils.datetime import the_same_time
+from arknights_mower.utils.log import logger
+
+
+class TaskTypes(Enum):
+ RUN_ORDER = ("run_order", "跑单", 1)
+ FIAMMETTA = ("菲亚梅塔", "肥鸭", 2)
+ SHIFT_OFF = ("shifit_off", "下班", 2)
+ SHIFT_ON = ("shifit_on", "上班", 2)
+ EXHAUST_OFF = ("exhaust_on", "用尽下班", 2)
+ SELF_CORRECTION = ("self_correction", "纠错", 2)
+ CLUE_PARTY = ("Impart", "趴体", 2)
+ MAA_MALL = ("maa_Mall", "MAA信用购物", 2)
+ NOT_SPECIFIC = ("", "空任务", 2)
+ RECRUIT = ("recruit", "公招", 2)
+ SKLAND = ("skland", "森空岛签到", 2)
+ RE_ORDER = ("宿舍排序", "宿舍排序", 2)
+ RELEASE_DORM = ("释放宿舍空位", "释放宿舍空位", 2)
+ REFRESH_TIME = ("强制刷新任务时间", "强制刷新任务时间", 2)
+ SKILL_UPGRADE = ("技能专精", "技能专精", 2)
+ DEPOT = ("仓库扫描", "仓库扫描", 2) # 但是我不会写剩下的
+
+ def __new__(cls, value, display_value, priority):
+ obj = object.__new__(cls)
+ obj._value_ = value
+ obj.display_value = display_value
+ obj.priority = priority
+ return obj
+
+
+def find_next_task(
+ tasks,
+ compare_time: datetime | None = None,
+ task_type="",
+ compare_type: Literal["<", "=", ">"] = "<",
+ meta_data="",
+):
+ """找符合条件的下一个任务
+
+ Args:
+ tasks: 任务列表
+ compare_time: 截止时间
+ """
+ if compare_type == "=":
+ return next(
+ (
+ e
+ for e in tasks
+ if the_same_time(e.time, compare_time)
+ and (True if task_type == "" else task_type == e.type)
+ and (True if meta_data == "" else meta_data in e.meta_data)
+ ),
+ None,
+ )
+ elif compare_type == ">":
+ return next(
+ (
+ e
+ for e in tasks
+ if (True if compare_time is None else e.time > compare_time)
+ and (True if task_type == "" else task_type == e.type)
+ and (True if meta_data == "" else meta_data in e.meta_data)
+ ),
+ None,
+ )
+ else:
+ return next(
+ (
+ e
+ for e in tasks
+ if (True if compare_time is None else e.time < compare_time)
+ and (True if task_type == "" else task_type == e.type)
+ and (True if meta_data == "" else meta_data in e.meta_data)
+ ),
+ None,
+ )
+
+
+def scheduling(tasks, run_order_delay=5, execution_time=0.75, time_now=None):
+ # execution_time per room
+ if time_now is None:
+ time_now = datetime.now()
+ if len(tasks) > 0:
+ tasks.sort(key=lambda x: x.time)
+
+ # 任务间隔最小时间(5分钟)
+ min_time_interval = timedelta(minutes=run_order_delay)
+
+ # 初始化变量以跟踪上一个优先级0任务和计划执行时间总和
+ last_priority_0_task = None
+ total_execution_time = 0
+
+ # 遍历任务列表
+ for i, task in enumerate(tasks):
+ current_time = time_now
+ # 判定任务堆积,如果第一个任务已经超时,则认为任务堆积
+ if task.type.priority == 1 and current_time > task.time:
+ total_execution_time += (current_time - task.time).total_seconds() / 60
+
+ if task.type.priority == 1:
+ if last_priority_0_task is not None:
+ time_difference = task.time - last_priority_0_task.time
+ if (
+ config.conf.run_order_grandet_mode.enable
+ and time_difference < min_time_interval
+ and time_now < last_priority_0_task.time
+ ):
+ logger.info("检测到跑单任务过于接近,准备修正跑单时间")
+ return last_priority_0_task
+ # 更新上一个优先级0任务和总执行时间
+ last_priority_0_task = task
+ total_execution_time = 0
+ else:
+ # 找到下一个优先级0任务的位置
+ next_priority_0_index = -1
+ for j in range(i + 1, len(tasks)):
+ if tasks[j].type.priority == 1:
+ next_priority_0_index = j
+ break
+ # 如果其他任务的总执行时间超过了下一个优先级0任务的执行时间,调整它们的时间
+ if next_priority_0_index > -1:
+ for j in range(i, next_priority_0_index):
+ # 菲亚充能/派对内置3分钟,线索购物内置1分钟
+ task_time = (
+ 0
+ if len(tasks[j].plan) > 0
+ and tasks[j].type
+ not in [TaskTypes.FIAMMETTA, TaskTypes.CLUE_PARTY]
+ else (
+ 3
+ if tasks[j].type
+ in [TaskTypes.FIAMMETTA, TaskTypes.CLUE_PARTY]
+ else 1
+ )
+ )
+ # 其他任务按照 每个房间*预设执行时间算 默认 45秒
+ estimate_time = (
+ len(tasks[j].plan) * execution_time
+ if task_time == 0
+ else task_time
+ )
+ if (
+ timedelta(minutes=total_execution_time + estimate_time)
+ + time_now
+ < tasks[j].time
+ ):
+ total_execution_time = 0
+ else:
+ total_execution_time += estimate_time
+ if (
+ timedelta(minutes=total_execution_time) + time_now
+ > tasks[next_priority_0_index].time
+ ):
+ logger.info("检测到任务可能影响到下次跑单修改任务至跑单之后")
+ logger.debug("||".join([str(t) for t in tasks]))
+ next_priority_0_time = tasks[next_priority_0_index].time
+ for j in range(i, next_priority_0_index):
+ tasks[j].time = next_priority_0_time + timedelta(seconds=1)
+ next_priority_0_time = tasks[j].time
+ logger.debug("||".join([str(t) for t in tasks]))
+ break
+ tasks.sort(key=lambda x: x.time)
+
+
+def try_add_release_dorm(plan, time, op_data, tasks):
+ if not op_data.config.free_room:
+ return
+ for k, v in plan.items():
+ for name in v:
+ if name != "Current":
+ _idx, __dorm = op_data.get_dorm_by_name(name)
+ if __dorm and __dorm.time < time:
+ add_release_dorm(tasks, op_data, name)
+
+
+def add_release_dorm(tasks, op_data, name):
+ _idx, __dorm = op_data.get_dorm_by_name(name)
+ if (
+ __dorm.time > datetime.now()
+ and find_next_task(tasks, task_type=TaskTypes.RELEASE_DORM, meta_data=name)
+ is None
+ ):
+ _free = op_data.operators[name]
+ if _free.current_room.startswith("dorm"):
+ __plan = {_free.current_room: ["Current"] * 5}
+ __plan[_free.current_room][_free.current_index] = "Free"
+ task = SchedulerTask(
+ time=__dorm.time,
+ task_type=TaskTypes.RELEASE_DORM,
+ task_plan=__plan,
+ meta_data=name,
+ )
+ tasks.append(task)
+ logger.info(name + " 新增释放宿舍任务")
+ logger.debug(str(task))
+
+
+def check_dorm_ordering(tasks, op_data):
+ # 仅当下班的时候才触发宿舍排序任务
+ plan = op_data.plan
+ if len(tasks) == 0:
+ return
+ if tasks[0].type == TaskTypes.SHIFT_OFF and tasks[0].meta_data == "":
+ extra_plan = {}
+ other_plan = {}
+ working_agent = []
+ for room, v in tasks[0].plan.items():
+ if not room.startswith("dorm"):
+ working_agent.extend(v)
+ for room, v in tasks[0].plan.items():
+ # 非宿舍则不需要清空
+ if room.startswith("dorm"):
+ # 是否检查过vip位置
+ pass_first_free = False
+ for idx, agent in enumerate(v):
+ # 如果当前位置非宿管 且无人员变动(有变动则是下班干员)
+ if "Free" == plan[room][idx].agent and agent == "Current":
+ # 如果高优先不变,则跳过逻辑判定
+ if not pass_first_free:
+ continue
+ current = next(
+ (
+ obj
+ for obj in op_data.operators.values()
+ if obj.current_room == room and obj.current_index == idx
+ ),
+ None,
+ )
+ if current:
+ if current.name not in working_agent:
+ v[idx] = current.name
+ else:
+ logger.debug(f"检测到干员{current.name}已经上班")
+ v[idx] = "Free"
+ if room not in extra_plan:
+ extra_plan[room] = copy.deepcopy(v)
+ # 新生成移除任务 --> 换成移除
+ extra_plan[room][idx] = ""
+ if "Free" == plan[room][idx].agent and not pass_first_free:
+ pass_first_free = True
+ else:
+ other_plan[room] = v
+ tasks[0].meta_data = "宿舍排序完成"
+ if extra_plan:
+ for k, v in other_plan.items():
+ del tasks[0].plan[k]
+ extra_plan[k] = v
+ logger.info("新增排序任务任务")
+ task = SchedulerTask(
+ task_plan=extra_plan,
+ time=tasks[0].time - timedelta(seconds=1),
+ task_type=TaskTypes.RE_ORDER,
+ )
+ tasks.insert(0, task)
+ logger.debug(str(task))
+
+
+def set_type_enum(value):
+ if value is None:
+ return TaskTypes.NOT_SPECIFIC
+ if isinstance(value, TaskTypes):
+ return value
+ if isinstance(value, str):
+ for task_type in TaskTypes:
+ if value.upper() == task_type.display_value.upper():
+ return task_type
+ return TaskTypes.NOT_SPECIFIC
+
+
+class SchedulerTask:
+ time = None
+ type = ""
+ plan = {}
+ meta_data = ""
+
+ def __init__(self, time=None, task_plan={}, task_type="", meta_data=""):
+ if time is None:
+ self.time = datetime.now()
+ else:
+ self.time = time
+ self.plan = task_plan
+ self.type = set_type_enum(task_type)
+ self.meta_data = meta_data
+
+ def format(self, time_offset=0):
+ res = copy.deepcopy(self)
+ res.time += timedelta(hours=time_offset)
+ res.type = res.type.display_value
+ if res.type == "空任务" and res.meta_data:
+ res.type = res.meta_data
+ return res
+
+ def __str__(self):
+ return f"SchedulerTask(time='{self.time}',task_plan={self.plan},task_type={self.type},meta_data='{self.meta_data}')"
+
+ def __eq__(self, other):
+ if isinstance(other, SchedulerTask):
+ return (
+ self.type == other.type
+ and self.plan == other.plan
+ and the_same_time(self.time, other.time)
+ )
+ return False
diff --git a/arknights_mower/utils/segment.py b/arknights_mower/utils/segment.py
index c6b6b28fb..0b7198f5f 100644
--- a/arknights_mower/utils/segment.py
+++ b/arknights_mower/utils/segment.py
@@ -1,18 +1,9 @@
-from __future__ import annotations
-
-import traceback
-
import cv2
import numpy as np
-from matplotlib import pyplot as plt
-from .image import saveimg
-from ..data import agent_list
-from ..ocr import ocrhandle
-from . import detector
-from . import typealias as tp
-from .log import logger
-from .recognize import RecognizeError
-import pytesseract
+
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import RecognizeError
class FloodCheckFailed(Exception):
@@ -22,10 +13,10 @@ class FloodCheckFailed(Exception):
def get_poly(x1: int, x2: int, y1: int, y2: int) -> tp.Rectangle:
x1, x2 = int(x1), int(x2)
y1, y2 = int(y1), int(y2)
- return np.array([ [ x1, y1 ], [ x1, y2 ], [ x2, y2 ], [ x2, y1 ] ])
+ return np.array([[x1, y1], [x1, y2], [x2, y2], [x2, y1]])
-def credit(img: tp.Image, draw: bool = False) -> list[ tp.Scope ]:
+def credit(img: tp.Image, draw: bool = False) -> list[tp.Scope]:
"""
信用交易所特供的图像分割算法
"""
@@ -33,25 +24,25 @@ def credit(img: tp.Image, draw: bool = False) -> list[ tp.Scope ]:
height, width, _ = img.shape
left, right = 0, width
- while np.max(img[ :, right - 1 ]) < 100:
+ while np.max(img[:, right - 1]) < 100:
right -= 1
- while np.max(img[ :, left ]) < 100:
+ while np.max(img[:, left]) < 100:
left += 1
def average(i: int) -> int:
num, sum = 0, 0
for j in range(left, right):
- if img[ i, j, 0 ] == img[ i, j, 1 ] and img[ i, j, 1 ] == img[ i, j, 2 ]:
+ if img[i, j, 0] == img[i, j, 1] and img[i, j, 1] == img[i, j, 2]:
num += 1
- sum += img[ i, j, 0 ]
+ sum += img[i, j, 0]
return sum // num
def ptp(j: int) -> int:
maxval = -999999
minval = 999999
for i in range(up_1, up_2):
- minval = min(minval, img[ i, j, 0 ])
- maxval = max(maxval, img[ i, j, 0 ])
+ minval = min(minval, img[i, j, 0])
+ maxval = max(maxval, img[i, j, 0])
return maxval - minval
up_1 = 0
@@ -67,7 +58,7 @@ def ptp(j: int) -> int:
up_2 += 1
down = height - 1
- while average(down) < 180:
+ while average(down) < 150:
down -= 1
right = width - 1
@@ -78,31 +69,35 @@ def ptp(j: int) -> int:
while ptp(left) < 50:
left += 1
- split_x = [ left + (right - left) // 5 * i for i in range(0, 6) ]
- split_y = [ up_1, (up_1 + down) // 2, down ]
+ split_x = [left + (right - left) // 5 * i for i in range(0, 6)]
+ split_y = [up_1, (up_1 + down) // 2, down]
- ret = [ ]
- for y1, y2 in zip(split_y[ :-1 ], split_y[ 1: ]):
- for x1, x2 in zip(split_x[ :-1 ], split_x[ 1: ]):
+ ret = []
+ for y1, y2 in zip(split_y[:-1], split_y[1:]):
+ for x1, x2 in zip(split_x[:-1], split_x[1:]):
ret.append(((x1, y1), (x2, y2)))
if draw:
- for y1, y2 in zip(split_y[ :-1 ], split_y[ 1: ]):
- for x1, x2 in zip(split_x[ :-1 ], split_x[ 1: ]):
- cv2.polylines(img, [ get_poly(x1, x2, y1, y2) ],
- True, 0, 10, cv2.LINE_AA)
+ for y1, y2 in zip(split_y[:-1], split_y[1:]):
+ for x1, x2 in zip(split_x[:-1], split_x[1:]):
+ cv2.polylines(
+ img, [get_poly(x1, x2, y1, y2)], True, 0, 10, cv2.LINE_AA
+ )
+
+ from matplotlib import pyplot as plt
+
plt.imshow(img)
plt.show()
- logger.debug(f'segment.credit: {ret}')
+ logger.debug(f"segment.credit: {ret}")
return ret
except Exception as e:
- logger.debug(traceback.format_exc())
+ logger.exception(e)
raise RecognizeError(e)
-def recruit(img: tp.Image, draw: bool = False) -> list[ tp.Scope ]:
+def recruit(img: tp.Image, draw: bool = False) -> list[tp.Scope]:
"""
公招特供的图像分割算法
"""
@@ -116,7 +111,7 @@ def adj_x(i: int) -> int:
sum = 0
for j in range(left, right):
for k in range(3):
- sum += abs(int(img[ i, j, k ]) - int(img[ i - 1, j, k ]))
+ sum += abs(int(img[i, j, k]) - int(img[i - 1, j, k]))
return sum // (right - left)
def adj_y(j: int) -> int:
@@ -125,19 +120,19 @@ def adj_y(j: int) -> int:
sum = 0
for i in range(up_2, down_2):
for k in range(3):
- sum += abs(int(img[ i, j, k ]) - int(img[ i, j - 1, k ]))
+ sum += abs(int(img[i, j, k]) - int(img[i, j - 1, k]))
return int(sum / (down_2 - up_2))
def average(i: int) -> int:
sum = 0
for j in range(left, right):
- sum += np.sum(img[ i, j, :3 ])
+ sum += np.sum(img[i, j, :3])
return sum // (right - left) // 3
def minus(i: int) -> int:
s = 0
for j in range(left, right):
- s += int(img[ i, j, 2 ]) - int(img[ i, j, 0 ])
+ s += int(img[i, j, 2]) - int(img[i, j, 0])
return s // (right - left)
up = 0
@@ -148,59 +143,65 @@ def minus(i: int) -> int:
up_2, down_2 = up - 90, up - 40
left = 0
- while np.max(img[ :, left ]) < 100:
+ while np.max(img[:, left]) < 100:
left += 1
left += 1
while adj_y(left) < 50:
left += 1
right = width - 1
- while np.max(img[ :, right ]) < 100:
+ while np.max(img[:, right]) < 100:
right -= 1
while adj_y(right) < 50:
right -= 1
- split_x = [ left, (left + right) // 2, right ]
+ split_x = [left, (left + right) // 2, right]
down = height - 1
- split_y = [ up, (up + down) // 2, down ]
+ split_y = [up, (up + down) // 2, down]
- ret = [ ]
- for y1, y2 in zip(split_y[ :-1 ], split_y[ 1: ]):
- for x1, x2 in zip(split_x[ :-1 ], split_x[ 1: ]):
+ ret = []
+ for y1, y2 in zip(split_y[:-1], split_y[1:]):
+ for x1, x2 in zip(split_x[:-1], split_x[1:]):
ret.append(((x1, y1), (x2, y2)))
if draw:
- for y1, y2 in zip(split_y[ :-1 ], split_y[ 1: ]):
- for x1, x2 in zip(split_x[ :-1 ], split_x[ 1: ]):
- cv2.polylines(img, [ get_poly(x1, x2, y1, y2) ],
- True, 0, 10, cv2.LINE_AA)
+ for y1, y2 in zip(split_y[:-1], split_y[1:]):
+ for x1, x2 in zip(split_x[:-1], split_x[1:]):
+ cv2.polylines(
+ img, [get_poly(x1, x2, y1, y2)], True, 0, 10, cv2.LINE_AA
+ )
+
+ from matplotlib import pyplot as plt
+
plt.imshow(img)
plt.show()
- logger.debug(f'segment.recruit: {ret}')
+ logger.debug(f"segment.recruit: {ret}")
return ret
except Exception as e:
- logger.debug(traceback.format_exc())
+ logger.exception(e)
raise RecognizeError(e)
-def base(img: tp.Image, central: tp.Scope, draw: bool = False) -> dict[ str, tp.Rectangle ]:
+def base(
+ img: tp.Image, central: tp.Scope, draw: bool = False
+) -> dict[str, tp.Rectangle]:
"""
基建布局的图像分割算法
"""
try:
ret = {}
- x1, y1 = central[ 0 ]
- x2, y2 = central[ 1 ]
+ x1, y1 = central[0]
+ x2, y2 = central[1]
alpha = (y2 - y1) / 160
x1 -= 170 * alpha
x2 += 182 * alpha
y1 -= 67 * alpha
y2 += 67 * alpha
central = get_poly(x1, x2, y1, y2)
- ret[ 'central' ] = central
+ ret["central"] = central
for i in range(1, 5):
y1 = y2 + 25 * alpha
@@ -209,372 +210,64 @@ def base(img: tp.Image, central: tp.Scope, draw: bool = False) -> dict[ str, tp.
dormitory = get_poly(x1, x2 - 158 * alpha, y1, y2)
else:
dormitory = get_poly(x1 + 158 * alpha, x2, y1, y2)
- ret[ f'dormitory_{i}' ] = dormitory
+ ret[f"dormitory_{i}"] = dormitory
- x1, y1 = ret[ 'dormitory_1' ][ 0 ]
- x2, y2 = ret[ 'dormitory_1' ][ 2 ]
+ x1, y1 = ret["dormitory_1"][0]
+ x2, y2 = ret["dormitory_1"][2]
x1 = x2 + 419 * alpha
x2 = x1 + 297 * alpha
factory = get_poly(x1, x2, y1, y2)
- ret[ f'factory' ] = factory
+ ret["factory"] = factory
y2 = y1 - 25 * alpha
y1 = y2 - 134 * alpha
meeting = get_poly(x1 - 158 * alpha, x2, y1, y2)
- ret[ f'meeting' ] = meeting
+ ret["meeting"] = meeting
y1 = y2 + 25 * alpha
y2 = y1 + 134 * alpha
y1 = y2 + 25 * alpha
y2 = y1 + 134 * alpha
contact = get_poly(x1, x2, y1, y2)
- ret[ f'contact' ] = contact
+ ret["contact"] = contact
y1 = y2 + 25 * alpha
y2 = y1 + 134 * alpha
train = get_poly(x1, x2, y1, y2)
- ret[ f'train' ] = train
+ ret["train"] = train
for floor in range(1, 4):
- x1, y1 = ret[ f'dormitory_{floor}' ][ 0 ]
- x2, y2 = ret[ f'dormitory_{floor}' ][ 2 ]
+ x1, y1 = ret[f"dormitory_{floor}"][0]
+ x2, y2 = ret[f"dormitory_{floor}"][2]
x2 = x1 - 102 * alpha
x1 = x2 - 295 * alpha
if floor & 1 == 0:
x2 = x1 - 24 * alpha
x1 = x2 - 295 * alpha
room = get_poly(x1, x2, y1, y2)
- ret[ f'room_{floor}_3' ] = room
+ ret[f"room_{floor}_3"] = room
x2 = x1 - 24 * alpha
x1 = x2 - 295 * alpha
room = get_poly(x1, x2, y1, y2)
- ret[ f'room_{floor}_2' ] = room
+ ret[f"room_{floor}_2"] = room
x2 = x1 - 24 * alpha
x1 = x2 - 295 * alpha
room = get_poly(x1, x2, y1, y2)
- ret[ f'room_{floor}_1' ] = room
+ ret[f"room_{floor}_1"] = room
if draw:
polys = list(ret.values())
cv2.polylines(img, polys, True, (255, 0, 0), 10, cv2.LINE_AA)
- plt.imshow(img)
- plt.show()
-
- logger.debug(f'segment.base: {ret}')
- return ret
-
- except Exception as e:
- logger.debug(traceback.format_exc())
- raise RecognizeError(e)
-def read_screen(img, type="mood", langurage="eng", limit=24, cord=None, change_color=False, draw=False) -> int:
- if cord is not None :
- img = img[ cord[1]:cord[3], cord[0]:cord[2] ]
- if 'mood' in type or type=="time":
- # 心情图片太小,复制8次提高准确率
- for x in range(0, 4):
- img = cv2.vconcat([img, img])
- if change_color: img[img == 137] = 255
- if draw : plt.imshow(img)
- if "mood" in type or type=='time':
- try:
- _config = r'-c tessedit_char_whitelist=0123456789: --psm 6'
- if type=='mood':_config = r'-c tessedit_char_whitelist=0123456789/- --psm 6'
- text = pytesseract.image_to_data(img,lang=langurage, config=_config, output_type='data.frame' )
- if type =='time': saveimg(img, 'data')
- text = text[text.conf != -1]
- lines = text.groupby(['page_num', 'block_num', 'par_num', 'line_num'], group_keys=True)['text'] \
- .apply(lambda x: ' '.join(map(str,list(x)))).tolist()
- confs = text.groupby(['page_num', 'block_num', 'par_num', 'line_num'], group_keys=True)['conf'].mean().tolist()
- line_conf = []
- for i in range(len(lines)):
- if lines[i].strip():
- line_conf.append((lines[i], round(confs[i], 3)))
- logger.debug(str(line_conf))
- __str = ''
- if 'mood' in type:
- if line_conf[len(line_conf) - 1][1] > 0.0 or (max(line_conf, key=lambda tup: tup[1])) == 0.0 or limit == 200:
- __str=line_conf[len(line_conf) - 1][0]
- else:
- __str = (max(line_conf, key=lambda tup: tup[1]))[0]
- else:
- _data = (max(line_conf, key=lambda tup: tup[1]))
- if _data[1]<70.0:
- __str = line_conf[len(line_conf) - 1][0]
- else:
- __str = _data[0]
- if '.0' in __str:
- __str = __str[0:__str.index('.0')]
- # else:
- # # 时间就找最大出现次数
- # x = [i[0] for i in line_conf]
- # __str = max(set(x), key=x.count)
- if "mood" in type:
- idx = 4
- if type =='mood' : idx = 3
- __str = __str[0:len(__str)-idx]
- if '/' in __str:
- __str= __str[0:__str.index('/')]
- if ''==__str:
- return 0
- number = int(__str)
- if number>limit:
- saveimg(img, 'error_mood')
- return limit
- return number
- else:
- return __str
- except Exception as e:
- # 空的时候是没人在基建
- logger.warning(f'读取错误:{__str}')
- saveimg(img, 'error_mood')
- return -1
-
-def worker(img: tp.Image, draw: bool = False) -> tuple[ list[ tp.Rectangle ], tp.Rectangle, bool ]:
- """
- 进驻总览的图像分割算法
- """
- try:
- height, width, _ = img.shape
-
- left, right = 0, width
- while np.max(img[ :, right - 1 ]) < 100:
- right -= 1
- while np.max(img[ :, left ]) < 100:
- left += 1
- x0 = right - 1
- while np.average(img[ :, x0, 1 ]) >= 100:
- x0 -= 1
- x0 -= 2
-
- seg = [ ]
- remove_mode = False
- pre, st = int(img[ 0, x0, 1 ]), 0
- for y in range(1, height):
- remove_mode |= int(img[ y, x0, 0 ]) - int(img[ y, x0, 1 ]) > 40
- if np.ptp(img[ y, x0 ]) <= 1 or int(img[ y, x0, 0 ]) - int(img[ y, x0, 1 ]) > 40:
- now = int(img[ y, x0, 1 ])
- if abs(now - pre) > 20:
- if now < pre and st == 0:
- st = y
- elif now > pre and st != 0:
- seg.append((st, y))
- st = 0
- pre = now
- elif st != 0:
- seg.append((st, y))
- st = 0
- # if st != 0:
- # seg.append((st, height))
- logger.debug(f'segment.worker: seg {seg}')
-
- remove_button = get_poly(x0 - 10, x0, seg[ 0 ][ 0 ], seg[ 0 ][ 1 ])
- seg = seg[ 1: ]
-
- for i in range(1, len(seg)):
- if seg[ i ][ 1 ] - seg[ i ][ 0 ] > 9:
- x1 = x0
- while img[ seg[ i ][ 1 ] - 3, x1 - 1, 2 ] < 100:
- x1 -= 1
- break
-
- ret = [ ]
- for i in range(len(seg)):
- if seg[ i ][ 1 ] - seg[ i ][ 0 ] > 9:
- ret.append(get_poly(x1, x0, seg[ i ][ 0 ], seg[ i ][ 1 ]))
+ from matplotlib import pyplot as plt
- if draw:
- cv2.polylines(img, ret, True, (255, 0, 0), 10, cv2.LINE_AA)
plt.imshow(img)
plt.show()
- logger.debug(f'segment.worker: {[ x.tolist() for x in ret ]}')
- return ret, remove_button, remove_mode
-
- except Exception as e:
- logger.debug(traceback.format_exc())
- raise RecognizeError(e)
-
-
-def agent(img, draw=False):
- """
- 干员总览的图像分割算法
- """
- try:
- height, width, _ = img.shape
- resolution = height
- left, right = 0, width
-
- # 异形屏适配
- while np.max(img[ :, right - 1 ]) < 100:
- right -= 1
- while np.max(img[ :, left ]) < 100:
- left += 1
-
- # 去除左侧干员详情
- x0 = left + 1
- while not (img[ height - 1, x0 - 1, 0 ] > img[ height - 1, x0, 0 ] + 10 and abs(
- int(img[ height - 1, x0, 0 ]) - int(img[ height - 1, x0 + 1, 0 ])) < 5):
- x0 += 1
-
- # ocr 初步识别干员名称
- ocr = ocrhandle.predict(img[ :, x0:right ])
-
- # 收集成功识别出来的干员名称识别结果,提取 y 范围,并将重叠的范围进行合并
- segs = [ (min(x[ 2 ][ 0 ][ 1 ], x[ 2 ][ 1 ][ 1 ]), max(x[ 2 ][ 2 ][ 1 ], x[ 2 ][ 3 ][ 1 ]))
- for x in ocr if x[ 1 ] in agent_list ]
- while True:
- _a, _b = None, None
- for i in range(len(segs)):
- for j in range(len(segs)):
- if i != j and (
- segs[ i ][ 0 ] <= segs[ j ][ 0 ] <= segs[ i ][ 1 ] or segs[ i ][ 0 ] <= segs[ j ][ 1 ] <=
- segs[ i ][ 1 ]):
- _a, _b = segs[ i ], segs[ j ]
- break
- if _b is not None:
- break
- if _b is not None:
- segs.remove(_a)
- segs.remove(_b)
- segs.append((min(_a[ 0 ], _b[ 0 ]), max(_a[ 1 ], _b[ 1 ])))
- else:
- break
- segs = sorted(segs)
-
- # 计算纵向的四个高度,[y0, y1] 是第一行干员名称的纵向坐标范围,[y2, y3] 是第二行干员名称的纵向坐标范围
- y0 = y1 = y2 = y3 = None
- for x in segs:
- if x[ 1 ] < height // 2:
- y0, y1 = x
- else:
- y2, y3 = x
- if y0 is None or y2 is None:
- raise RecognizeError
- hpx = y1 - y0 # 卡片上干员名称的高度
- logger.debug((segs, [ y0, y1, y2, y3 ]))
-
- # 预计算:横向坐标范围集合
- x_set = set()
- for x in ocr:
- if x[ 1 ] in agent_list and (y0 <= x[ 2 ][ 0 ][ 1 ] <= y1 or y2 <= x[ 2 ][ 0 ][ 1 ] <= y3):
- # 只考虑矩形右边端点
- x_set.add(x[ 2 ][ 1 ][ 0 ])
- x_set.add(x[ 2 ][ 2 ][ 0 ])
- x_set = sorted(x_set)
- logger.debug(x_set)
-
- # 排除掉一些重叠的范围,获得最终的横向坐标范围
- gap = 160 * (resolution / 1080) # 卡片宽度下限
- x_set = [ x_set[ 0 ] ] + \
- [ y for x, y in zip(x_set[ :-1 ], x_set[ 1: ]) if y - x > gap ]
- gap = [ y - x for x, y in zip(x_set[ :-1 ], x_set[ 1: ]) ]
- logger.debug(sorted(gap))
- gap = int(np.median(gap)) # 干员卡片宽度
- for x, y in zip(x_set[ :-1 ], x_set[ 1: ]):
- if y - x > gap:
- gap_num = round((y - x) / gap)
- for i in range(1, gap_num):
- x_set.append(int(x + (y - x) / gap_num * i))
- x_set = sorted(x_set)
- if x_set[ -1 ] - x_set[ -2 ] < gap:
- # 如果最后一个间隔不足宽度则丢弃,避免出现「梅尔」只露出一半识别成「梅」算作成功识别的情况
- x_set = x_set[ :-1 ]
- while np.min(x_set) > 0:
- x_set.append(np.min(x_set) - gap)
- while np.max(x_set) < right - x0:
- x_set.append(np.max(x_set) + gap)
- x_set = sorted(x_set)
- logger.debug(x_set)
-
- # 获得所有的干员名称对应位置
- ret = [ ]
- for x1, x2 in zip(x_set[ :-1 ], x_set[ 1: ]):
- if 0 <= x1 + hpx and x0 + x2 + 5 <= right:
- ret += [ get_poly(x0 + x1 + hpx, x0 + x2 + 5, y0, y1),
- get_poly(x0 + x1 + hpx, x0 + x2 + 5, y2, y3) ]
-
- # draw for debug
- if draw:
- __img = img.copy()
- cv2.polylines(__img, ret, True, (255, 0, 0), 3, cv2.LINE_AA)
- plt.imshow(__img)
- plt.show()
-
- logger.debug(f'segment.agent: {[ x.tolist() for x in ret ]}')
- return ret, ocr
-
- except Exception as e:
- logger.debug(traceback.format_exc())
- raise RecognizeError(e)
-
-
-def free_agent(img, draw=False):
- """
- 识别未在工作中的干员
- """
- try:
- height, width, _ = img.shape
- resolution = height
- left, right = 0, width
-
- # 异形屏适配
- while np.max(img[ :, right - 1 ]) < 100:
- right -= 1
- while np.max(img[ :, left ]) < 100:
- left += 1
-
- # 去除左侧干员详情
- x0 = left + 1
- while not (img[ height - 1, x0 - 1, 0 ] > img[ height - 1, x0, 0 ] + 10 and abs(
- int(img[ height - 1, x0, 0 ]) - int(img[ height - 1, x0 + 1, 0 ])) < 5):
- x0 += 1
-
- # 获取分割结果
- ret = agent(img, draw)
- st = ret[ -2 ][ 2 ] # 起点
- ed = ret[ 0 ][ 1 ] # 终点
-
- # 收集 y 坐标并初步筛选
- y_set = set()
- __ret = [ ]
- for poly in ret:
- __img = img[ poly[ 0, 1 ]:poly[ 2, 1 ], poly[ 0, 0 ]:poly[ 2, 0 ] ]
- y_set.add(poly[ 0, 1 ])
- y_set.add(poly[ 2, 1 ])
- # 去除空白的干员框
- if 80 <= np.min(__img):
- logger.debug(f'drop(empty): {poly.tolist()}')
- continue
- # 去除被选中的蓝框
- elif np.count_nonzero(__img[ :, :, 0 ] >= 224) == 0 or np.count_nonzero(__img[ :, :, 0 ] == 0) > 0:
- logger.debug(f'drop(selected): {poly.tolist()}')
- continue
- __ret.append(poly)
- ret = __ret
-
- y1, y2, y4, y5 = sorted(list(y_set))
- y0 = height - y5
- y3 = y0 - y2 + y5
-
- ret_free = [ ]
- for poly in ret:
- poly[ :, 1 ][ poly[ :, 1 ] == y1 ] = y0
- poly[ :, 1 ][ poly[ :, 1 ] == y4 ] = y3
- __img = img[ poly[ 0, 1 ]:poly[ 2, 1 ], poly[ 0, 0 ]:poly[ 2, 0 ] ]
- if not detector.is_on_shift(__img):
- ret_free.append(poly)
-
- if draw:
- __img = img.copy()
- cv2.polylines(__img, ret_free, True, (255, 0, 0), 3, cv2.LINE_AA)
- plt.imshow(__img)
- plt.show()
-
- logger.debug(f'segment.free_agent: {[ x.tolist() for x in ret_free ]}')
- return ret_free, st, ed
+ logger.debug(f"segment.base: {ret}")
+ return ret
except Exception as e:
- logger.debug(traceback.format_exc())
+ logger.exception(e)
raise RecognizeError(e)
diff --git a/arknights_mower/utils/simulator.py b/arknights_mower/utils/simulator.py
new file mode 100644
index 000000000..7f474f114
--- /dev/null
+++ b/arknights_mower/utils/simulator.py
@@ -0,0 +1,124 @@
+import subprocess
+from enum import Enum
+from os import system
+
+from arknights_mower import __system__
+from arknights_mower.utils import config
+from arknights_mower.utils.csleep import MowerExit, csleep
+from arknights_mower.utils.log import logger
+
+
+class Simulator_Type(Enum):
+ Nox = "夜神"
+ MuMu12 = "MuMu12"
+ Leidian9 = "雷电9"
+ Waydroid = "Waydroid"
+ ReDroid = "ReDroid"
+ MuMuPro = "MuMuPro"
+ Genymotion = "Genymotion"
+
+
+def restart_simulator(stop=True, start=True):
+ data = config.conf.simulator
+ index = data.index
+ simulator_type = data.name
+ simulator_folder = data.simulator_folder
+ wait_time = data.wait_time
+ hotkey = data.hotkey
+ cmd = ""
+ blocking = False
+
+ if simulator_type not in Simulator_Type:
+ logger.warning(f"尚未支持{simulator_type}重启/自动启动")
+ csleep(10)
+ return
+
+ if simulator_type == Simulator_Type.Nox.value:
+ cmd = "Nox.exe"
+ if int(index) >= 0:
+ cmd += f" -clone:Nox_{index}"
+ cmd += " -quit"
+ elif simulator_type == Simulator_Type.MuMu12.value:
+ cmd = "MuMuManager.exe api -v "
+ if int(index) >= 0:
+ cmd += f"{index} "
+ cmd += "shutdown_player"
+ elif simulator_type == Simulator_Type.Waydroid.value:
+ cmd = "waydroid session stop"
+ elif simulator_type == Simulator_Type.Leidian9.value:
+ cmd = "ldconsole.exe quit --index "
+ if int(index) >= 0:
+ cmd += f"{index} "
+ else:
+ cmd += "0"
+ elif simulator_type == Simulator_Type.ReDroid.value:
+ cmd = f"docker stop {index} -t 0"
+ elif simulator_type == Simulator_Type.MuMuPro.value:
+ cmd = f"Contents/MacOS/mumutool close {index}"
+ elif simulator_type == Simulator_Type.Genymotion.value:
+ if __system__ == "windows":
+ cmd = "gmtool.exe"
+ elif __system__ == "darwin":
+ cmd = "Contents/MacOS/gmtool"
+ else:
+ cmd = "./gmtool"
+ cmd += f' admin stop "{index}"'
+ blocking = True
+
+ if stop:
+ logger.info(f"关闭{simulator_type}模拟器")
+ exec_cmd(cmd, simulator_folder, 3, blocking)
+ if simulator_type == "MuMu12" and config.conf.fix_mumu12_adb_disconnect:
+ logger.info("结束adb进程")
+ system("taskkill /f /t /im adb.exe")
+
+ if start:
+ if simulator_type == Simulator_Type.Nox.value:
+ cmd = cmd.replace(" -quit", "")
+ elif simulator_type == Simulator_Type.MuMu12.value:
+ cmd = cmd.replace(" shutdown_player", " launch_player")
+ elif simulator_type == Simulator_Type.Waydroid.value:
+ cmd = "waydroid show-full-ui"
+ elif simulator_type == Simulator_Type.Leidian9.value:
+ cmd = cmd.replace("quit", "launch")
+ elif simulator_type == Simulator_Type.ReDroid.value:
+ cmd = f"docker start {index}"
+ elif simulator_type == Simulator_Type.MuMuPro.value:
+ cmd = cmd.replace("close", "open")
+ elif simulator_type == Simulator_Type.Genymotion.value:
+ cmd = cmd.replace("stop", "start", 1)
+ logger.info(f"启动{simulator_type}模拟器")
+ exec_cmd(cmd, simulator_folder, wait_time, blocking)
+ if hotkey:
+ hotkey = hotkey.split("+")
+ import pyautogui
+
+ pyautogui.FAILSAFE = False
+ pyautogui.hotkey(*hotkey)
+
+
+def exec_cmd(cmd, folder_path, wait_time, blocking):
+ logger.debug(cmd)
+ process = subprocess.Popen(
+ cmd,
+ shell=True,
+ cwd=folder_path,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+
+ if not blocking:
+ csleep(wait_time)
+ process.terminate()
+ return
+ while wait_time > 0:
+ try:
+ csleep(0)
+ logger.debug(process.communicate(timeout=1))
+ break
+ except MowerExit:
+ raise
+ except subprocess.TimeoutExpired:
+ wait_time -= 1
diff --git a/arknights_mower/utils/solver.py b/arknights_mower/utils/solver.py
index 9f7491534..606258082 100644
--- a/arknights_mower/utils/solver.py
+++ b/arknights_mower/utils/solver.py
@@ -1,51 +1,97 @@
-from __future__ import annotations
-
+import random
+import sys
import time
-import traceback
from abc import abstractmethod
-
-from ..utils import typealias as tp
-from . import config, detector
-from .device import Device, KeyCode
-from .log import logger
-from .recognize import RecognizeError, Recognizer, Scene
+from datetime import datetime, timedelta
+from typing import Literal, Optional, Tuple
+
+import cv2
+import numpy as np
+
+from arknights_mower.data import scene_list
+from arknights_mower.utils import config
+from arknights_mower.utils import typealias as tp
+from arknights_mower.utils.csleep import MowerExit, csleep
+from arknights_mower.utils.device.adb_client.const import KeyCode
+from arknights_mower.utils.device.adb_client.session import Session
+from arknights_mower.utils.device.device import Device
+from arknights_mower.utils.device.scrcpy import Scrcpy
+from arknights_mower.utils.email import send_message
+from arknights_mower.utils.image import cropimg, thres2
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.recognize import RecognizeError, Recognizer, Scene
+from arknights_mower.utils.simulator import restart_simulator
+from arknights_mower.utils.traceback import caller_info
class StrategyError(Exception):
- """ Strategy Error """
+ """Strategy Error"""
+
pass
class BaseSolver:
- """ Base class, provide basic operation """
-
- def __init__(self, device: Device = None, recog: Recognizer = None) -> None:
+ """Base class, provide basic operation"""
+
+ tap_info = None, None
+ waiting_scene = [
+ Scene.CONNECTING,
+ Scene.UNKNOWN,
+ Scene.LOADING,
+ Scene.LOGIN_LOADING,
+ Scene.LOGIN_MAIN_NOENTRY,
+ Scene.OPERATOR_ONGOING,
+ ]
+
+ def __init__(
+ self,
+ device: Device | None = None,
+ recog: Recognizer | None = None,
+ ) -> None:
# self.device = device if device is not None else (recog.device if recog is not None else Device())
if device is None and recog is not None:
raise RuntimeError
- self.device = device if device is not None else Device()
+ if device is not None:
+ self.device = device
+ else:
+ while True:
+ try:
+ self.device = Device()
+ self.device.client.check_server_alive()
+ Session().connect(config.conf.adb)
+ if not self.device.check_resolution():
+ raise MowerExit
+ if config.conf.droidcast.enable:
+ self.device.start_droidcast()
+ if config.conf.touch_method == "scrcpy":
+ self.device.control.scrcpy = Scrcpy(self.device.client)
+ break
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(e)
+ restart_simulator()
+
self.recog = recog if recog is not None else Recognizer(self.device)
- self.device.check_current_focus()
- def run(self)-> None:
+ def run(self) -> None:
+ self.check_current_focus()
retry_times = config.MAX_RETRYTIME
- result =None
+ result = None
while retry_times > 0:
try:
result = self.transition()
if result:
return result
+ except MowerExit:
+ raise
except RecognizeError as e:
- logger.warning(f'识别出了点小差错 qwq: {e}')
- self.recog.save_screencap('failure')
+ logger.exception(f"识别出了点小差错 qwq: {e}")
retry_times -= 1
self.sleep(3)
continue
- except StrategyError as e:
- logger.error(e)
- logger.debug(traceback.format_exc())
- return
except Exception as e:
+ logger.exception(e)
raise e
retry_times = config.MAX_RETRYTIME
@@ -55,36 +101,47 @@ def transition(self) -> bool:
return True # means task completed
def get_color(self, pos: tp.Coordinate) -> tp.Pixel:
- """ get the color of the pixel """
+ """get the color of the pixel"""
return self.recog.color(pos[0], pos[1])
- def get_pos(self, poly: tp.Location, x_rate: float = 0.5, y_rate: float = 0.5) -> tp.Coordinate:
- """ get the pos form tp.Location """
+ @staticmethod
+ def get_pos(
+ poly: tp.Location, x_rate: float = 0.5, y_rate: float = 0.5
+ ) -> tp.Coordinate:
+ """get the pos form tp.Location"""
if poly is None:
- raise RecognizeError('poly is empty')
+ raise RecognizeError("poly is empty")
elif len(poly) == 4:
# tp.Rectangle
- x = (poly[0][0] * (1-x_rate) + poly[1][0] * (1-x_rate) +
- poly[2][0] * x_rate + poly[3][0] * x_rate) / 2
- y = (poly[0][1] * (1-y_rate) + poly[3][1] * (1-y_rate) +
- poly[1][1] * y_rate + poly[2][1] * y_rate) / 2
+ x = (
+ poly[0][0] * (1 - x_rate)
+ + poly[1][0] * (1 - x_rate)
+ + poly[2][0] * x_rate
+ + poly[3][0] * x_rate
+ ) / 2
+ y = (
+ poly[0][1] * (1 - y_rate)
+ + poly[3][1] * (1 - y_rate)
+ + poly[1][1] * y_rate
+ + poly[2][1] * y_rate
+ ) / 2
elif len(poly) == 2 and isinstance(poly[0], (list, tuple)):
# tp.Scope
- x = poly[0][0] * (1-x_rate) + poly[1][0] * x_rate
- y = poly[0][1] * (1-y_rate) + poly[1][1] * y_rate
+ x = poly[0][0] * (1 - x_rate) + poly[1][0] * x_rate
+ y = poly[0][1] * (1 - y_rate) + poly[1][1] * y_rate
else:
# tp.Coordinate
x, y = poly
return (int(x), int(y))
- def sleep(self, interval: float = 1, rebuild: bool = True) -> None:
- """ sleeping for a interval """
- time.sleep(interval)
- self.recog.update(rebuild=rebuild)
+ def sleep(self, interval: float = 1) -> None:
+ """sleeping for a interval"""
+ csleep(interval)
+ self.recog.update()
def input(self, referent: str, input_area: tp.Scope, text: str = None) -> None:
- """ input text """
- logger.debug(f'input: {referent} {input_area}')
+ """input text"""
+ logger.debug(f"input: {referent} {input_area}")
self.device.tap(self.get_pos(input_area))
time.sleep(0.5)
if text is None:
@@ -92,41 +149,184 @@ def input(self, referent: str, input_area: tp.Scope, text: str = None) -> None:
self.device.send_text(str(text))
self.device.tap((0, 0))
- def find(self, res: str, draw: bool = False, scope: tp.Scope = None, thres: int = None, judge: bool = True, strict: bool = False) -> tp.Scope:
- return self.recog.find(res, draw, scope, thres, judge, strict)
-
- def tap(self, poly: tp.Location, x_rate: float = 0.5, y_rate: float = 0.5, interval: float = 1, rebuild: bool = True) -> None:
- """ tap """
+ def find(
+ self,
+ res: tp.Res,
+ draw: bool = False,
+ scope: tp.Scope = None,
+ thres: int = None,
+ judge: bool = True,
+ strict: bool = False,
+ score=0.0,
+ ) -> tp.Scope:
+ return self.recog.find(res, draw, scope, thres, judge, strict, score)
+
+ def tap(
+ self,
+ poly: tp.Location,
+ x_rate: float = 0.5,
+ y_rate: float = 0.5,
+ interval: float = 1,
+ ) -> None:
+ """tap"""
+ if config.stop_mower.is_set():
+ raise MowerExit
pos = self.get_pos(poly, x_rate, y_rate)
self.device.tap(pos)
if interval > 0:
- self.sleep(interval, rebuild)
-
- def tap_element(self, element_name: str, x_rate: float = 0.5, y_rate: float = 0.5, interval: float = 1, rebuild: bool = True,
- draw: bool = False, scope: tp.Scope = None, judge: bool = True, detected: bool = False) -> bool:
- """ tap element """
- if element_name == 'nav_button':
- element = self.recog.nav_button()
+ self.sleep(interval)
+
+ def ctap(self, pos: tp.Location, max_seconds: int = 10):
+ id = caller_info()
+ logger.debug(id)
+ now = datetime.now()
+ lid, ltime = self.tap_info
+ if lid != id or (lid == id and now - ltime > timedelta(seconds=max_seconds)):
+ self.tap_info = id, now
+ self.tap(pos)
else:
- element = self.find(element_name, draw, scope, judge=judge)
+ self.sleep()
+
+ def check_current_focus(self):
+ self.recog.check_current_focus()
+
+ def tap_element(
+ self,
+ element_name: tp.Res,
+ x_rate: float = 0.5,
+ y_rate: float = 0.5,
+ interval: float = 1,
+ score: float = 0.0,
+ draw: bool = False,
+ scope: tp.Scope = None,
+ judge: bool = True,
+ detected: bool = False,
+ thres: Optional[int] = None,
+ ) -> bool:
+ """tap element"""
+ element = self.find(
+ element_name, draw, scope, judge=judge, score=score, thres=thres
+ )
if detected and element is None:
return False
- self.tap(element, x_rate, y_rate, interval, rebuild)
+ self.tap(element, x_rate, y_rate, interval)
return True
- def swipe(self, start: tp.Coordinate, movement: tp.Coordinate, duration: int = 100, interval: float = 1, rebuild: bool = True) -> None:
- """ swipe """
+ def tap_index_element(
+ self,
+ name: Literal[
+ "friend",
+ "infrastructure",
+ "mission",
+ "recruit",
+ "shop",
+ "terminal",
+ "warehouse",
+ "headhunting",
+ "mail",
+ ],
+ ):
+ pos = {
+ "friend": (544, 862), # 好友
+ "infrastructure": (1545, 948), # 基建
+ "mission": (1201, 904), # 任务
+ "recruit": (1507, 774), # 公开招募
+ "shop": (1251, 727), # 采购中心
+ "terminal": (1458, 297), # 终端
+ "warehouse": (1788, 973), # 仓库
+ "headhunting": (1749, 783), # 干员寻访
+ "mail": (292, 62), # 邮件
+ }
+ self.ctap(pos[name])
+
+ def tap_nav_element(
+ self,
+ name: Literal[
+ "index",
+ "terminal",
+ "infrastructure",
+ "recruit",
+ "headhunting",
+ "shop",
+ "mission",
+ "friend",
+ ],
+ ):
+ pos = {
+ "index": (140, 365), # 首页
+ "terminal": (793, 163), # 终端
+ "infrastructure": (1030, 163), # 基建
+ "recruit": (1435, 365), # 公开招募
+ "headhunting": (1623, 364), # 干员寻访
+ "shop": (1804, 362), # 采购中心
+ "mission": (1631, 53), # 任务
+ "friend": (1801, 53), # 好友
+ }
+ self.ctap(pos[name])
+
+ def tap_terminal_button(
+ self,
+ name: Literal[
+ "main",
+ "main_theme",
+ "intermezzi",
+ "biography",
+ "collection",
+ "regular",
+ "longterm",
+ "contract",
+ ],
+ ):
+ y = 1005
+ pos = {
+ "main": (115, y), # 首页
+ "main_theme": (356, y), # 主题曲
+ "intermezzi": (596, y), # 插曲
+ "biography": (836, y), # 别传
+ "collection": (1077, y), # 资源收集
+ "regular": (1317, y), # 常态事务
+ "longterm": (1556, y), # 长期探索
+ "contract": (1796, y), # 危机合约
+ }
+ self.ctap(pos[name])
+
+ def template_match(
+ self,
+ res: str,
+ scope: Optional[tp.Scope] = None,
+ method: int = cv2.TM_CCOEFF_NORMED,
+ ) -> Tuple[float, tp.Scope]:
+ return self.recog.template_match(res, scope, method)
+
+ def swipe(
+ self,
+ start: tp.Coordinate,
+ movement: tp.Coordinate,
+ duration: int = 100,
+ interval: float = 1,
+ ) -> None:
+ """swipe"""
+ if config.stop_mower.is_set():
+ raise MowerExit
end = (start[0] + movement[0], start[1] + movement[1])
self.device.swipe(start, end, duration=duration)
if interval > 0:
- self.sleep(interval, rebuild)
-
- def swipe_only(self, start: tp.Coordinate, movement: tp.Coordinate, duration: int = 100, interval: float = 1) -> None:
- """ swipe only, no rebuild and recapture """
+ self.sleep(interval)
+
+ def swipe_only(
+ self,
+ start: tp.Coordinate,
+ movement: tp.Coordinate,
+ duration: int = 100,
+ interval: float = 1,
+ ) -> None:
+ """swipe only, no rebuild and recapture"""
+ if config.stop_mower.is_set():
+ raise MowerExit
end = (start[0] + movement[0], start[1] + movement[1])
self.device.swipe(start, end, duration=duration)
if interval > 0:
- time.sleep(interval)
+ csleep(interval)
# def swipe_seq(self, points: list[tp.Coordinate], duration: int = 100, interval: float = 1, rebuild: bool = True) -> None:
# """ swipe with point sequence """
@@ -143,39 +343,166 @@ def swipe_only(self, start: tp.Coordinate, movement: tp.Coordinate, duration: in
# if interval > 0:
# self.sleep(interval, rebuild)
- def swipe_noinertia(self, start: tp.Coordinate, movement: tp.Coordinate, duration: int = 100, interval: float = 1, rebuild: bool = False) -> None:
- """ swipe with no inertia (movement should be vertical) """
+ def swipe_noinertia(
+ self,
+ start: tp.Coordinate,
+ movement: tp.Coordinate,
+ duration: int = 20,
+ interval: float = 0.2,
+ ) -> None:
+ """swipe with no inertia (movement should be vertical)"""
+ if config.stop_mower.is_set():
+ raise MowerExit
points = [start]
if movement[0] == 0:
dis = abs(movement[1])
- points.append((start[0]+100, start[1]))
- points.append((start[0]+100, start[1]+movement[1]))
- points.append((start[0], start[1]+movement[1]))
+ points.append((start[0] + 100, start[1]))
+ points.append((start[0] + 100, start[1] + movement[1]))
+ points.append((start[0], start[1] + movement[1]))
else:
dis = abs(movement[0])
- points.append((start[0], start[1]+100))
- points.append((start[0]+movement[0], start[1]+100))
- points.append((start[0]+movement[0], start[1]))
- self.device.swipe_ext(points, durations=[200, dis*duration//100, 200])
+ points.append((start[0], start[1] + 100))
+ points.append((start[0] + movement[0], start[1] + 100))
+ points.append((start[0] + movement[0], start[1]))
+ self.device.swipe_ext(points, durations=[200, dis * duration // 100, 200])
if interval > 0:
- self.sleep(interval, rebuild)
+ self.sleep(interval)
- def back(self, interval: float = 1, rebuild: bool = True) -> None:
- """ send back keyevent """
+ def back(self, interval: float = 1) -> None:
+ """send back keyevent"""
self.device.send_keyevent(KeyCode.KEYCODE_BACK)
- self.sleep(interval, rebuild)
+ self.sleep(interval)
def scene(self) -> int:
- """ get the current scene in the game """
+ """get the current scene in the game"""
return self.recog.get_scene()
- def get_infra_scene(self) -> int:
- """ get the current scene in the infra """
- return self.recog.get_infra_scene()
+ def ra_scene(self) -> int:
+ """
+ 生息演算场景识别
+ """
+ return self.recog.get_ra_scene()
+
+ def sf_scene(self) -> int:
+ """
+ 隐秘战线场景识别
+ """
+ return self.recog.get_sf_scene()
+
+ def sss_scene(self) -> int:
+ """
+ 保全导航场景识别
+ """
+ return self.recog.get_sss_scene()
+
+ def train_scene(self) -> int:
+ """
+ 训练室景识别
+ """
+ return self.recog.get_train_scene()
def is_login(self):
- """ check if you are logged in """
- return not (self.scene() // 100 == 1 or self.scene() // 100 == 99 or self.scene() == -1)
+ """check if you are logged in"""
+ return not (
+ (scene := self.scene()) // 100 == 1 or scene // 100 == 99 or scene == -1
+ )
+
+ def solve_captcha(self, refresh=False):
+ th = thres2(self.recog.gray, 254)
+ pos = np.nonzero(th)
+ offset_x = pos[1].min()
+ offset_y = pos[0].min()
+ img_scope = ((offset_x, offset_y), (pos[1].max(), pos[0].max()))
+ img = cropimg(self.recog.img, img_scope)
+ h = img.shape[0]
+
+ def _t(ratio):
+ return int(h * ratio)
+
+ def _p(ratio_x, ratio_y):
+ return _t(ratio_x), _t(ratio_y)
+
+ if refresh:
+ logger.info("刷新验证码")
+ self.tap((offset_x + _t(0.189), offset_y + _t(0.916)), interval=3)
+ img = cropimg(self.recog.img, img_scope)
+
+ left_part = cropimg(img, (_p(0.032, 0.032), _p(0.202, 0.591)))
+ hsv = cv2.cvtColor(left_part, cv2.COLOR_RGB2HSV)
+ mask = cv2.inRange(hsv, (25, 0, 0), (35, 255, 255))
+
+ tpl_width = _t(0.148)
+ tpl_height = _t(0.135)
+ tpl_border = _t(0.0056)
+ tpl_padding = _t(0.018)
+ tpl = np.zeros((tpl_height, tpl_width), np.uint8)
+ tpl[:] = (255,)
+ tpl[
+ tpl_border : tpl_height - tpl_border,
+ tpl_border : tpl_width - tpl_border,
+ ] = (0,)
+
+ result = cv2.matchTemplate(mask, tpl, cv2.TM_SQDIFF, None, tpl)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ x, y = min_loc
+
+ source_p = (
+ (x + tpl_padding, y + tpl_padding),
+ (x + tpl_width - tpl_padding, y + tpl_height - tpl_padding),
+ )
+ source = cropimg(left_part, source_p)
+ mask = cropimg(mask, source_p)
+ right_part = cropimg(
+ img,
+ (
+ (_t(0.201), _t(0.032) + source_p[0][1]),
+ (_t(0.94), _t(0.032) + source_p[1][1]),
+ ),
+ )
+
+ for _y in range(source.shape[0]):
+ for _x in range(source.shape[1]):
+ for _c in range(source.shape[2]):
+ source[_y, _x, _c] = np.clip(source[_y, _x, _c] * 0.7 - 23, 0, 255)
+
+ mask = cv2.bitwise_not(mask)
+ result = cv2.matchTemplate(right_part, source, cv2.TM_SQDIFF_NORMED, None, mask)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+ x = _t(0.201) + min_loc[0] - _t(0.032) - x - tpl_padding + _t(0.128)
+ x += random.choice([-4, -3, -2, 2, 3, 4])
+
+ def _rb(R, r):
+ return random.random() * _t(R) + _t(r)
+
+ btn_x = _rb(0.1, 0.01)
+ start = offset_x + btn_x + _t(0.128), offset_y + _rb(0.1, 0.01) + _t(0.711)
+ end = offset_x + btn_x + x, offset_y + _rb(0.1, 0.01) + _t(0.711)
+ p1 = end[0] + _rb(0.1, 0.02), end[1] + _rb(0.05, 0.02)
+ p2 = end[0] + _rb(0.1, 0.02), end[1] + _rb(0.05, 0.02)
+
+ logger.info("滑动验证码")
+ self.device.swipe_ext(
+ (start, p1, p2, end, end),
+ durations=[
+ random.randint(400, 600),
+ random.randint(200, 300),
+ random.randint(200, 300),
+ random.randint(200, 300),
+ ],
+ )
+
+ def bilibili(self):
+ """B服登录/隐私政策界面点击确认/同意"""
+ img = cv2.cvtColor(self.recog.img, cv2.COLOR_RGB2HSV)
+ img = cv2.inRange(img, (96, 150, 0), (100, 255, 255))
+ contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+ rect = [cv2.boundingRect(c) for c in contours]
+ if len(rect) == 0:
+ self.sleep()
+ return
+ rect.sort(key=lambda c: c[2], reverse=True)
+ x, y, w, h = rect[0]
+ self.tap(((x, y), (x + w, y + h)))
def login(self):
"""
@@ -184,54 +511,67 @@ def login(self):
retry_times = config.MAX_RETRYTIME
while retry_times and not self.is_login():
try:
- if self.scene() == Scene.LOGIN_START:
- self.tap((self.recog.w // 2, self.recog.h - 10), 3)
- elif self.scene() == Scene.LOGIN_QUICKLY:
- self.tap_element('login_awake')
- elif self.scene() == Scene.LOGIN_MAIN:
- self.tap_element('login_account', 0.25)
- elif self.scene() == Scene.LOGIN_REGISTER:
+ if (scene := self.scene()) == Scene.LOGIN_START:
+ # 应对两种情况:
+ # 1. 点击左上角“网络检测”后出现“您即将进行一次网络拨测,该操作将采集您的网络状态并上报,点击确认继续”,点x
+ # 2. 点击左上角“清除缓存”之后取消
+ self.tap((665, 741))
+ elif scene == Scene.LOGIN_NEW:
+ self.tap_element("login_new")
+ elif scene == Scene.LOGIN_BILIBILI:
+ self.bilibili()
+ elif scene == Scene.LOGIN_BILIBILI_PRIVACY:
+ self.bilibili()
+ elif scene == Scene.LOGIN_QUICKLY:
+ self.tap_element("login_awake")
+ elif scene == Scene.LOGIN_MAIN:
+ self.tap_element("login_account", 0.25)
+ elif scene == Scene.LOGIN_REGISTER:
self.back(2)
- elif self.scene() == Scene.LOGIN_CAPTCHA:
- exit()
- # self.back(600) # TODO: Pending
- elif self.scene() == Scene.LOGIN_INPUT:
- input_area = self.find('login_username')
+ elif scene == Scene.LOGIN_CAPTCHA:
+ captcha_times = 3
+ while captcha_times > 0:
+ self.solve_captcha(captcha_times < 3)
+ self.sleep(5)
+ if self.find("login_captcha"):
+ captcha_times -= 1
+ else:
+ break
+ if captcha_times <= 0:
+ send_message(
+ "验证码自动滑动失败,退出游戏,停止运行mower", level="ERROR"
+ )
+ self.device.exit()
+ sys.exit()
+ elif scene == Scene.LOGIN_INPUT:
+ input_area = self.find("login_username")
if input_area is not None:
- self.input('Enter username: ', input_area, config.USERNAME)
- input_area = self.find('login_password')
+ self.input("Enter username: ", input_area, config.USERNAME)
+ input_area = self.find("login_password")
if input_area is not None:
- self.input('Enter password: ', input_area, config.PASSWORD)
- self.tap_element('login_button')
- elif self.scene() == Scene.LOGIN_ANNOUNCE:
- self.tap_element('login_iknow')
- elif self.scene() == Scene.LOGIN_LOADING:
- self.sleep(3)
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.scene() == Scene.CONFIRM:
- self.tap(detector.confirm(self.recog.img))
- elif self.scene() == Scene.LOGIN_MAIN_NOENTRY:
- self.sleep(3)
- elif self.scene() == Scene.LOGIN_CADPA_DETAIL:
+ self.input("Enter password: ", input_area, config.PASSWORD)
+ self.tap_element("login_button")
+ elif scene == Scene.LOGIN_ANNOUNCE:
+ self.tap_element("login_iknow")
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ elif scene == Scene.CONFIRM:
+ self.tap((960, 740))
+ elif scene == Scene.LOGIN_CADPA_DETAIL:
self.back(2)
- elif self.scene() == Scene.LOGIN_BILIBILI:
- self.tap_element('login_bilibili_entry')
- elif self.scene() == Scene.NETWORK_CHECK:
- self.tap_element('double_confirm', 0.2)
- elif self.scene() == Scene.UNKNOWN:
- raise RecognizeError('Unknown scene')
+ elif scene == Scene.UNKNOWN:
+ raise RecognizeError("Unknown scene")
else:
- raise RecognizeError('Unanticipated scene')
+ raise RecognizeError("Unanticipated scene")
+ except MowerExit:
+ raise
except RecognizeError as e:
- logger.warning(f'识别出了点小差错 qwq: {e}')
- self.recog.save_screencap('failure')
+ logger.exception(f"识别出了点小差错 qwq: {e}")
retry_times -= 1
self.sleep(3)
continue
except Exception as e:
+ logger.exception(e)
raise e
retry_times = config.MAX_RETRYTIME
@@ -246,80 +586,138 @@ def get_navigation(self):
while retry_times:
if self.scene() == Scene.NAVIGATION_BAR:
return True
- elif not self.tap_element('nav_button', detected=True):
+ elif not self.tap_element("nav_button", detected=True):
return False
retry_times -= 1
+ def back_to_infrastructure(self):
+ self.back_to_index()
+ self.tap_index_element("infrastructure")
+
def back_to_index(self):
"""
返回主页
"""
- logger.info('back to index')
+ logger.info("back to index")
retry_times = config.MAX_RETRYTIME
pre_scene = None
- while retry_times and self.scene() != Scene.INDEX:
+ while retry_times and (scene := self.scene()) != Scene.INDEX:
try:
if self.get_navigation():
- self.tap_element('nav_index')
- elif self.scene() == Scene.CLOSE_MINE:
- self.tap_element('close_mine')
- elif self.scene() == Scene.ANNOUNCEMENT:
- self.tap(detector.announcement_close(self.recog.img))
- elif self.scene() == Scene.MATERIEL:
- self.tap_element('materiel_ico')
- elif self.scene() // 100 == 1:
+ self.tap_nav_element("index")
+ elif scene == Scene.RIIC_REPORT:
+ self.tap((100, 60))
+ elif scene == Scene.ANNOUNCEMENT:
+ self.tap(self.recog.check_announcement())
+ elif scene == Scene.MATERIEL:
+ self.tap_element("materiel_ico")
+ elif scene // 100 == 1:
self.login()
- elif self.scene() == Scene.CONFIRM:
- self.tap(detector.confirm(self.recog.img))
- elif self.scene() == Scene.LOADING:
- self.sleep(3)
- elif self.scene() == Scene.CONNECTING:
- self.sleep(3)
- elif self.scene() == Scene.SKIP:
- self.tap_element('skip')
- elif self.scene() == Scene.OPERATOR_ONGOING:
+ elif scene == Scene.CONFIRM:
+ self.tap((960, 740))
+ elif scene in self.waiting_scene:
+ self.waiting_solver()
+ elif scene == Scene.SKIP:
+ self.tap_element("skip")
+ elif scene == Scene.OPERATOR_ONGOING:
self.sleep(10)
- elif self.scene() == Scene.OPERATOR_FINISH:
+ elif scene == Scene.OPERATOR_FINISH:
self.tap((self.recog.w // 2, 10))
- elif self.scene() == Scene.OPERATOR_ELIMINATE_FINISH:
+ elif scene == Scene.OPERATOR_ELIMINATE_FINISH:
self.tap((self.recog.w // 2, 10))
- elif self.scene() == Scene.DOUBLE_CONFIRM:
- self.tap_element('double_confirm', 0.8)
- elif self.scene() == Scene.NETWORK_CHECK:
- self.tap_element('double_confirm', 0.2)
- elif self.scene() == Scene.MAIL:
- mail = self.find('mail')
- mid_y = (mail[0][1] + mail[1][1]) // 2
- self.tap((mid_y, mid_y))
- elif self.scene() == Scene.INFRA_ARRANGE_CONFIRM:
+ elif scene in [
+ Scene.DOUBLE_CONFIRM,
+ Scene.EXIT_GAME,
+ Scene.BACK_TO_FRIEND_LIST,
+ Scene.OPERATOR_GIVEUP,
+ Scene.LEAVE_INFRASTRUCTURE,
+ Scene.REFRESH_TAGS,
+ ]:
+ self.tap_element("double_confirm/main", x_rate=1)
+ elif scene == Scene.RECRUIT_AGENT:
+ self.tap((self.recog.w // 2, self.recog.h // 2))
+ elif scene == Scene.MAIL:
+ self.back()
+ elif scene == Scene.INFRA_ARRANGE_CONFIRM:
self.tap((self.recog.w // 3, self.recog.h - 10))
- elif self.scene() == Scene.UNKNOWN:
- raise RecognizeError('Unknown scene')
- elif pre_scene is None or pre_scene != self.scene():
- pre_scene = self.scene()
+ elif scene == Scene.UNKNOWN:
+ raise RecognizeError("Unknown scene")
+ elif pre_scene is None or pre_scene != scene:
+ pre_scene = scene
self.back()
else:
- raise RecognizeError('Unanticipated scene')
+ raise RecognizeError("Unanticipated scene")
+ except MowerExit:
+ raise
except RecognizeError as e:
- logger.warning(f'识别出了点小差错 qwq: {e}')
- self.recog.save_screencap('failure')
+ logger.exception(f"识别出了点小差错 qwq: {e}")
retry_times -= 1
self.sleep(3)
continue
except Exception as e:
+ logger.exception(e)
raise e
retry_times = config.MAX_RETRYTIME
if self.scene() != Scene.INDEX:
raise StrategyError
- def back_to_reclamation_algorithm(self):
- self.recog.update()
- while self.find('index_terminal') is None:
- if self.scene() == Scene.UNKNOWN:
- self.device.exit('com.hypergryph.arknights')
- self.back_to_index()
- logger.info('导航至生息演算')
- self.tap_element('index_terminal', 0.5)
- self.tap((self.recog.w*0.2, self.recog.h*0.8),interval=0.5)
+ def to_sss(self):
+ """保全导航"""
+ logger.info("保全导航")
+ start_time = datetime.now()
+ while (scene := self.sss_scene()) not in [Scene.SSS_DEPLOY, Scene.SSS_REDEPLOY]:
+ if datetime.now() - start_time > timedelta(minutes=1):
+ return "保全导航超时"
+ try:
+ if scene == Scene.INDEX:
+ self.tap_index_element("terminal")
+ elif scene == Scene.TERMINAL_MAIN:
+ self.tap_terminal_button("regular")
+ elif scene == Scene.TERMINAL_REGULAR:
+ self.tap((1548, 870))
+ elif scene == Scene.SSS_MAIN:
+ self.tap((384, 324) if config.conf.sss.type == 1 else (768, 648))
+ elif scene == Scene.SSS_START:
+ self.tap_element("sss/start_button")
+ elif scene == Scene.SSS_EC:
+ if config.conf.sss.ec == 1:
+ ec_x = 576
+ elif config.conf.sss.ec == 2:
+ ec_x = 960
+ else:
+ ec_x = 1344
+ self.tap((ec_x, 540))
+ self.tap_element("sss/ec_button")
+ elif scene == Scene.SSS_DEVICE:
+ self.tap_element("sss/device_button")
+ elif scene == Scene.SSS_SQUAD:
+ self.tap_element("sss/squad_button")
+ elif scene == Scene.SSS_GUIDE:
+ self.tap_element("sss/close_button")
+ else:
+ self.sleep()
+ except MowerExit:
+ raise
+ except Exception as e:
+ logger.exception(f"保全导航出错:{e}")
+ logger.info(
+ f"保全导航成功,用时{(datetime.now() - start_time).total_seconds():.0f}秒"
+ )
+
+ def waiting_solver(self):
+ """需要等待的页面解决方法。触发超时重启会返回False"""
+ scene = self.scene()
+ sleep_time, wait_count = getattr(
+ config.conf.waiting_scene, scene_list[str(scene)]["label"]
+ )
+ for _ in range(wait_count):
+ self.sleep(sleep_time)
+ if self.scene() != scene:
+ return True
+ logger.warning("相同场景等待超时")
+ self.device.exit()
+ csleep(3)
+ self.check_current_focus()
+ return False
diff --git a/arknights_mower/utils/tile_pos.py b/arknights_mower/utils/tile_pos.py
new file mode 100644
index 000000000..7650ead2a
--- /dev/null
+++ b/arknights_mower/utils/tile_pos.py
@@ -0,0 +1,236 @@
+import lzma
+import math
+import pickle
+from dataclasses import dataclass
+from typing import Any, List, Optional, Tuple
+
+import numpy as np
+import numpy.typing as npt
+
+from arknights_mower import __rootdir__
+
+
+@dataclass
+class Tile:
+ heightType: int
+ buildableType: int
+
+
+@dataclass
+class Vector3:
+ x: float
+ y: float
+ z: float
+
+ def clone(self) -> "Vector3":
+ return Vector3(self.x, self.y, self.z)
+
+
+@dataclass
+class Vector2:
+ x: float
+ y: float
+
+ def clone(self) -> "Vector2":
+ return Vector2(self.x, self.y)
+
+
+@dataclass
+class Level:
+ stageId: str
+ code: str
+ levelId: str
+ name: str
+ height: int
+ width: int
+ tiles: List[List[Tile]] = None
+ view: List[List[int]] = None
+
+ @classmethod
+ def from_json(cls, json_data: dict[Any, Any]) -> "Level":
+ raw_tiles = json_data["tiles"]
+ tiles = []
+ for row in raw_tiles:
+ row_tiles = []
+ for tile in row:
+ row_tiles.append(Tile(tile["heightType"], tile["buildableType"]))
+ tiles.append(row_tiles)
+
+ return cls(
+ stageId=json_data["stageId"],
+ code=json_data["code"],
+ levelId=json_data["levelId"],
+ name=json_data["name"],
+ height=json_data["height"],
+ width=json_data["width"],
+ tiles=tiles,
+ view=json_data["view"],
+ )
+
+ def get_width(self):
+ return self.width
+
+ def get_height(self):
+ return self.height
+
+ def get_tile(self, row: int, col: int) -> Optional[Tile]:
+ if 0 <= row <= self.height and 0 <= col <= self.width:
+ return self.tiles[row][col]
+ return None
+
+
+class Calc:
+ screen_width: int
+ screen_height: int
+ ratio: float
+
+ view: Vector3
+ view_side: Vector3
+ level: Level
+
+ matrix_p: npt.NDArray[np.float32]
+ matrix_x: npt.NDArray[np.float32]
+ matrix_y: npt.NDArray[np.float32]
+
+ def __init__(self, screen_width: int, screen_height: int, level: Level):
+ self.screen_width = screen_width
+ self.screen_height = screen_height
+ self.ratio = screen_height / screen_width
+ self.level = level
+ self.matrix_p = np.array(
+ [
+ [self.ratio / math.tan(math.pi * 20 / 180), 0, 0, 0],
+ [0, 1 / math.tan(math.pi * 20 / 180), 0, 0],
+ [0, 0, -(1000 + 0.3) / (1000 - 0.3), -(1000 * 0.3 * 2) / (1000 - 0.3)],
+ [0, 0, -1, 0],
+ ]
+ )
+ self.matrix_x = np.array(
+ [
+ [1, 0, 0, 0],
+ [0, math.cos(math.pi * 30 / 180), -math.sin(math.pi * 30 / 180), 0],
+ [0, -math.sin(math.pi * 30 / 180), -math.cos(math.pi * 30 / 180), 0],
+ [0, 0, 0, 1],
+ ]
+ )
+ self.matrix_y = np.array(
+ [
+ [math.cos(math.pi * 10 / 180), 0, math.sin(math.pi * 10 / 180), 0],
+ [0, 1, 0, 0],
+ [-math.sin(math.pi * 10 / 180), 0, math.cos(math.pi * 10 / 180), 0],
+ [0, 0, 0, 1],
+ ]
+ )
+ self.view = Vector3(level.view[0][0], level.view[0][1], level.view[0][2])
+ self.view_side = Vector3(level.view[1][0], level.view[1][1], level.view[1][2])
+
+ def adapter(self) -> Tuple[float, float]:
+ fromRatio = 9 / 16
+ toRatio = 3 / 4
+ if self.ratio < fromRatio - 0.00001:
+ return 0, 0
+ t = (self.ratio - fromRatio) / (toRatio - fromRatio)
+ return -1.4 * t, -2.8 * t
+
+ def get_focus_offset(self, tile_x: int, tile_y: int) -> Vector3:
+ x = tile_x - (self.level.width - 1) / 2
+ y = (self.level.height - 1) / 2 - tile_y
+ return Vector3(x, y, 0)
+
+ def get_character_world_pos(self, tile_x: int, tile_y: int) -> Vector3:
+ x = tile_x - (self.level.width - 1) / 2
+ y = (self.level.height - 1) / 2 - tile_y
+ tile = self.level.get_tile(tile_y, tile_x)
+ assert tile is not None
+ z = tile.heightType * -0.4
+ return Vector3(x, y, z)
+
+ def get_with_draw_world_pos(self, tile_x: int, tile_y: int) -> Vector3:
+ ret = self.get_character_world_pos(tile_x, tile_y)
+ ret.x -= 1.3143386840820312
+ ret.y += 1.314337134361267
+ ret.z = -0.3967874050140381
+ return ret
+
+ def get_skill_world_pos(self, tile_x: int, tile_y: int) -> Vector3:
+ ret = self.get_character_world_pos(tile_x, tile_y)
+ ret.x += 1.3143386840820312
+ ret.y -= 1.314337134361267
+ ret.z = -0.3967874050140381
+ return ret
+
+ def get_character_screen_pos(
+ self, tile_x: int, tile_y: int, side: bool = False, focus: bool = False
+ ) -> Vector2:
+ if focus:
+ side = True
+ world_pos = self.get_character_world_pos(tile_x, tile_y)
+ if focus:
+ offset = self.get_focus_offset(tile_x, tile_y)
+ else:
+ offset = Vector3(0.0, 0.0, 0.0)
+ return self.world_to_screen_pos(world_pos, side, offset)
+
+ def get_with_draw_screen_pos(self, tile_x: int, tile_y: int) -> Vector2:
+ world_pos = self.get_with_draw_world_pos(tile_x, tile_y)
+ offset = self.get_focus_offset(tile_x, tile_y)
+ return self.world_to_screen_pos(world_pos, True, offset)
+
+ def get_skill_screen_pos(self, tile_x: int, tile_y: int) -> Vector2:
+ world_pos = self.get_skill_world_pos(tile_x, tile_y)
+ offset = self.get_focus_offset(tile_x, tile_y)
+ return self.world_to_screen_pos(world_pos, True, offset)
+
+ def world_to_screen_matrix(
+ self, side: bool = False, offset: Optional[Vector3] = None
+ ) -> npt.NDArray[np.float32]:
+ if offset is None:
+ offset = Vector3(0.0, 0.0, 0.0)
+ adapter_y, adapter_z = self.adapter()
+ if side:
+ x, y, z = self.view_side.x, self.view_side.y, self.view_side.z
+ else:
+ x, y, z = self.view.x, self.view.y, self.view.z
+ x += offset.x
+ y += offset.y + adapter_y
+ z += offset.z + adapter_z
+ raw = np.array(
+ [
+ [1, 0, 0, -x],
+ [0, 1, 0, -y],
+ [0, 0, 1, -z],
+ [0, 0, 0, 1],
+ ],
+ np.float32,
+ )
+ if side:
+ matrix = np.dot(self.matrix_x, self.matrix_y)
+ matrix = np.dot(matrix, raw)
+ else:
+ matrix = np.dot(self.matrix_x, raw)
+ return np.dot(self.matrix_p, matrix)
+
+ def world_to_screen_pos(
+ self, pos: Vector3, side: bool = False, offset: Optional[Vector3] = None
+ ) -> Vector2:
+ matrix = self.world_to_screen_matrix(side, offset)
+ x, y, _, w = np.dot(matrix, np.array([pos.x, pos.y, pos.z, 1]))
+ x = (1 + x / w) / 2
+ y = (1 + y / w) / 2
+ return Vector2(x * self.screen_width, (1 - y) * self.screen_height)
+
+
+LEVELS: List[Level] = []
+with lzma.open(f"{__rootdir__}/models/levels.pkl", "rb") as f:
+ level_table = pickle.load(f)
+for data in level_table:
+ LEVELS.append(Level.from_json(data))
+
+
+def find_level(code: Optional[str], name: Optional[str]) -> Optional[Level]:
+ for level in LEVELS:
+ if code is not None and code == level.code:
+ return level
+ if name is not None and name == level.name:
+ return level
+ return None
diff --git a/arknights_mower/utils/traceback.py b/arknights_mower/utils/traceback.py
new file mode 100644
index 000000000..72c9fd5ce
--- /dev/null
+++ b/arknights_mower/utils/traceback.py
@@ -0,0 +1,14 @@
+from inspect import getframeinfo, stack
+from pathlib import Path
+
+from arknights_mower.utils.path import get_path
+
+
+def caller_info():
+ caller = getframeinfo(stack()[2][0])
+ relative_name = Path(caller.filename)
+ try:
+ relative_name = relative_name.relative_to(get_path("@install"))
+ except ValueError:
+ pass
+ return f"{relative_name}:{caller.lineno}"
diff --git a/arknights_mower/utils/typealias.py b/arknights_mower/utils/typealias.py
deleted file mode 100644
index 5241658a8..000000000
--- a/arknights_mower/utils/typealias.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from typing import Dict, List, Tuple, Union
-
-import numpy as np
-from numpy.typing import NDArray
-
-# Image
-Image = NDArray[np.int8]
-Pixel = Tuple[int, int, int]
-
-GrayImage = NDArray[np.int8]
-GrayPixel = int
-
-# Recognizer
-Range = Tuple[int, int]
-Coordinate = Tuple[int, int]
-Scope = Tuple[Coordinate, Coordinate]
-Slice = Tuple[Range, Range]
-Rectangle = Tuple[Coordinate, Coordinate, Coordinate, Coordinate]
-Location = Union[Rectangle, Scope, Coordinate]
-
-# Matcher
-Hash = List[int]
-Score = Tuple[float, float, float, float]
-
-# Operation Plan
-OpePlan = Tuple[str, int]
-
-# BaseConstruct Plan
-BasePlan = Dict[str, List[str]]
-
-# Parameter
-ParamArgs = List[str]
diff --git a/arknights_mower/utils/typealias/__init__.py b/arknights_mower/utils/typealias/__init__.py
new file mode 100644
index 000000000..2f57ed21d
--- /dev/null
+++ b/arknights_mower/utils/typealias/__init__.py
@@ -0,0 +1,36 @@
+from typing import Dict, List, Tuple, Union
+
+import numpy as np
+from numpy.typing import NDArray
+
+from .res import Res
+
+__all__ = ["Res"]
+
+# Image
+Image = NDArray[np.int8]
+Pixel = NDArray[np.int8]
+
+GrayImage = NDArray[np.int8]
+GrayPixel = int
+
+# Recognizer
+Range = Tuple[int, int]
+Coordinate = Tuple[int, int]
+Scope = Tuple[Coordinate, Coordinate]
+Slice = Tuple[Range, Range]
+Rectangle = Tuple[Coordinate, Coordinate, Coordinate, Coordinate]
+Location = Union[Rectangle, Scope, Coordinate]
+
+# Matcher
+Hash = List[int]
+Score = Tuple[float, float, float, float]
+
+# Operation Plan
+OpePlan = Tuple[str, int]
+
+# BaseConstruct Plan
+BasePlan = Dict[str, List[str]]
+
+# Parameter
+ParamArgs = List[str]
diff --git a/arknights_mower/utils/typealias/res.py b/arknights_mower/utils/typealias/res.py
new file mode 100644
index 000000000..c05c83fc4
--- /dev/null
+++ b/arknights_mower/utils/typealias/res.py
@@ -0,0 +1,545 @@
+from typing import Literal
+
+Res = Literal[
+ "12cadpa",
+ "1800",
+ "all_in",
+ "announcement_close",
+ "arrange_blue_yes",
+ "arrange_check_in",
+ "arrange_check_in_on",
+ "arrange_confirm",
+ "arrange_order_options",
+ "arrange_order_options_scene",
+ "bill_accelerate",
+ "biography",
+ "business_card",
+ "clue",
+ "clue_next",
+ "collection",
+ "collection_small",
+ "confirm",
+ "confirm_blue",
+ "connecting",
+ "control_central",
+ "control_central_assistants",
+ "credit_shop_countdown",
+ "credit_visiting",
+ "depot",
+ "depot_empty",
+ "drone",
+ "enter",
+ "episode",
+ "factory_accelerate",
+ "factory_collect",
+ "friend_list",
+ "friend_visit",
+ "hypergryph",
+ "index_nav",
+ "infra_collect_bill",
+ "infra_collect_factory",
+ "infra_collect_trust",
+ "infra_exp_complete",
+ "infra_gold_complete",
+ "infra_lmd_complete",
+ "infra_no_operator",
+ "infra_notification",
+ "infra_ori_complete",
+ "infra_oru_complete",
+ "infra_overview",
+ "infra_overview_in",
+ "infra_todo",
+ "infra_trust_complete",
+ "loading",
+ "loading2",
+ "loading3",
+ "loading4",
+ "loading7",
+ "login_account",
+ "login_awake",
+ "login_bilibili",
+ "login_bilibili_privacy",
+ "login_button",
+ "login_captcha",
+ "login_connecting",
+ "login_iknow",
+ "login_loading",
+ "login_logo",
+ "login_new",
+ "login_password",
+ "login_username",
+ "mail",
+ "main_theme",
+ "main_theme_small",
+ "materiel_ico",
+ "mission_collect",
+ "mission_daily",
+ "mission_daily_on",
+ "mission_trainee_on",
+ "mission_weekly",
+ "mission_weekly_on",
+ "nav_bar",
+ "nav_button",
+ "one_hour",
+ "ope_agency",
+ "ope_agency_fail",
+ "ope_agency_going",
+ "ope_agency_lock",
+ "ope_elimi_agency",
+ "ope_elimi_agency_confirm",
+ "ope_elimi_agency_panel",
+ "ope_elimi_finished",
+ "ope_eliminate",
+ "ope_failed",
+ "ope_finish",
+ "ope_plan",
+ "ope_recover",
+ "ope_recover_originite",
+ "ope_recover_originite_on",
+ "ope_recover_potion",
+ "ope_recover_potion_on",
+ "ope_select_start",
+ "ope_start",
+ "open_recruitment",
+ "order_label",
+ "order_ready",
+ "pull_once",
+ "read_mail",
+ "recruiting_instructions",
+ "room_detail",
+ "sanity",
+ "shop_assist",
+ "shop_cart",
+ "shop_collect",
+ "shop_credit",
+ "shop_credit_2",
+ "shop_credit_not_enough",
+ "skill_collect_confirm",
+ "skill_confirm",
+ "skip",
+ "spent_credit",
+ "start",
+ "stone",
+ "terminal_eliminate",
+ "terminal_longterm",
+ "terminal_longterm_reclamation_algorithm",
+ "terminal_main",
+ "terminal_pre",
+ "terminal_pre2",
+ "terminal_regular",
+ "train_main",
+ "training_completed",
+ "training_support",
+ "upgrade",
+ "upgrade_failure",
+ "visit_limit",
+ "yes",
+ "read_and_agree",
+ "ope_select_start_empty",
+ "next_step",
+ "agent_name/char_009_12fce",
+ "agent_name/char_010_chen",
+ "agent_name/char_017_huang",
+ "agent_name/char_102_texas",
+ "agent_name/char_103_angel",
+ "agent_name/char_107_liskam",
+ "agent_name/char_108_silent",
+ "agent_name/char_109_fmout",
+ "agent_name/char_112_siege",
+ "agent_name/char_115_headbr",
+ "agent_name/char_117_myrrh",
+ "agent_name/char_118_yuki",
+ "agent_name/char_120_hibisc",
+ "agent_name/char_121_lava",
+ "agent_name/char_122_beagle",
+ "agent_name/char_123_fang",
+ "agent_name/char_124_kroos",
+ "agent_name/char_126_shotst",
+ "agent_name/char_127_estell",
+ "agent_name/char_128_plosis",
+ "agent_name/char_129_bluep",
+ "agent_name/char_130_doberm",
+ "agent_name/char_133_mm",
+ "agent_name/char_134_ifrit",
+ "agent_name/char_136_hsguma",
+ "agent_name/char_137_brownb",
+ "agent_name/char_141_nights",
+ "agent_name/char_143_ghost",
+ "agent_name/char_144_red",
+ "agent_name/char_145_prove",
+ "agent_name/char_147_shining",
+ "agent_name/char_148_nearl",
+ "agent_name/char_149_scave",
+ "agent_name/char_150_snakek",
+ "agent_name/char_151_myrtle",
+ "agent_name/char_155_tiger",
+ "agent_name/char_158_milu",
+ "agent_name/char_163_hpsts",
+ "agent_name/char_164_nightm",
+ "agent_name/char_171_bldsk",
+ "agent_name/char_172_svrash",
+ "agent_name/char_173_slchan",
+ "agent_name/char_174_slbell",
+ "agent_name/char_179_cgbird",
+ "agent_name/char_181_flower",
+ "agent_name/char_183_skgoat",
+ "agent_name/char_185_frncat",
+ "agent_name/char_188_helage",
+ "agent_name/char_190_clour",
+ "agent_name/char_192_falco",
+ "agent_name/char_193_frostl",
+ "agent_name/char_195_glassb",
+ "agent_name/char_196_sunbr",
+ "agent_name/char_197_poca",
+ "agent_name/char_199_yak",
+ "agent_name/char_2013_cerber",
+ "agent_name/char_201_moeshd",
+ "agent_name/char_202_demkni",
+ "agent_name/char_204_platnm",
+ "agent_name/char_208_melan",
+ "agent_name/char_210_stward",
+ "agent_name/char_211_adnach",
+ "agent_name/char_212_ansel",
+ "agent_name/char_213_mostma",
+ "agent_name/char_215_mantic",
+ "agent_name/char_218_cuttle",
+ "agent_name/char_219_meteo",
+ "agent_name/char_222_bpipe",
+ "agent_name/char_225_haak",
+ "agent_name/char_226_hmau",
+ "agent_name/char_235_jesica",
+ "agent_name/char_236_rope",
+ "agent_name/char_237_gravel",
+ "agent_name/char_240_wyvern",
+ "agent_name/char_241_panda",
+ "agent_name/char_242_otter",
+ "agent_name/char_243_waaifu",
+ "agent_name/char_248_mgllan",
+ "agent_name/char_250_phatom",
+ "agent_name/char_253_greyy",
+ "agent_name/char_254_vodfox",
+ "agent_name/char_258_podego",
+ "agent_name/char_261_sddrag",
+ "agent_name/char_263_skadi",
+ "agent_name/char_271_spikes",
+ "agent_name/char_272_strong",
+ "agent_name/char_274_astesi",
+ "agent_name/char_277_sqrrel",
+ "agent_name/char_278_orchid",
+ "agent_name/char_279_excu",
+ "agent_name/char_281_popka",
+ "agent_name/char_282_catap",
+ "agent_name/char_283_midn",
+ "agent_name/char_284_spot",
+ "agent_name/char_285_medic2",
+ "agent_name/char_286_cast3",
+ "agent_name/char_289_gyuki",
+ "agent_name/char_290_vigna",
+ "agent_name/char_293_thorns",
+ "agent_name/char_294_ayer",
+ "agent_name/char_298_susuro",
+ "agent_name/char_301_cutter",
+ "agent_name/char_302_glaze",
+ "agent_name/char_306_leizi",
+ "agent_name/char_308_swire",
+ "agent_name/char_326_glacus",
+ "agent_name/char_328_cammou",
+ "agent_name/char_337_utage",
+ "agent_name/char_340_shwaz",
+ "agent_name/char_343_tknogi",
+ "agent_name/char_344_beewax",
+ "agent_name/char_346_aosta",
+ "agent_name/char_349_chiave",
+ "agent_name/char_350_surtr",
+ "agent_name/char_356_broca",
+ "agent_name/char_358_lisa",
+ "agent_name/char_365_aprl",
+ "agent_name/char_366_acdrop",
+ "agent_name/char_367_swllow",
+ "agent_name/char_373_lionhd",
+ "agent_name/char_376_therex",
+ "agent_name/char_378_asbest",
+ "agent_name/char_379_sesa",
+ "agent_name/char_381_bubble",
+ "agent_name/char_385_finlpp",
+ "agent_name/char_4000_jnight",
+ "agent_name/char_400_weedy",
+ "agent_name/char_401_elysm",
+ "agent_name/char_4093_frston",
+ "agent_name/char_4136_phonor",
+ "agent_name/char_415_flint",
+ "agent_name/char_416_zumama",
+ "agent_name/char_423_blemsh",
+ "agent_name/char_500_noirc",
+ "agent_name/char_501_durin",
+ "agent_name/char_502_nblade",
+ "agent_name/char_503_rang",
+ "clue/1",
+ "clue/2",
+ "clue/3",
+ "clue/4",
+ "clue/5",
+ "clue/6",
+ "clue/7",
+ "clue/badge_new",
+ "clue/button_get",
+ "clue/button_unlock",
+ "clue/daily",
+ "clue/filter_all",
+ "clue/give_away",
+ "clue/icon_notification",
+ "clue/label_give_away",
+ "clue/receive",
+ "clue/summary",
+ "clue/title_party",
+ "depot_num/digit_0",
+ "depot_num/digit_1",
+ "depot_num/digit_2",
+ "depot_num/digit_3",
+ "depot_num/digit_4",
+ "depot_num/digit_5",
+ "depot_num/digit_6",
+ "depot_num/digit_7",
+ "depot_num/digit_8",
+ "depot_num/digit_9",
+ "depot_num/digit_91",
+ "depot_num/digit_point",
+ "double_confirm/exit",
+ "double_confirm/friend",
+ "double_confirm/give_up",
+ "double_confirm/infrastructure",
+ "double_confirm/main",
+ "double_confirm/network",
+ "double_confirm/recruit",
+ "double_confirm/voice",
+ "drone_count/0",
+ "drone_count/1",
+ "drone_count/2",
+ "drone_count/3",
+ "drone_count/4",
+ "drone_count/5",
+ "drone_count/6",
+ "drone_count/7",
+ "drone_count/8",
+ "drone_count/9",
+ "fight/c",
+ "fight/c_mask",
+ "fight/choose",
+ "fight/complete",
+ "fight/enemy",
+ "fight/failed_text",
+ "fight/kills_separator",
+ "fight/pause",
+ "fight/refresh",
+ "fight/skill_ready",
+ "fight/use",
+ "navigation/entry",
+ "navigation/episode",
+ "navigation/ope_difficulty",
+ "navigation/ope_hard",
+ "navigation/ope_hard_small",
+ "navigation/ope_normal",
+ "navigation/ope_normal_small",
+ "navigation/record_restoration",
+ "orders_time/0",
+ "orders_time/1",
+ "orders_time/2",
+ "orders_time/3",
+ "orders_time/4",
+ "orders_time/5",
+ "orders_time/6",
+ "orders_time/7",
+ "orders_time/8",
+ "orders_time/9",
+ "ra/action_points",
+ "ra/adventure",
+ "ra/adventure_ok",
+ "ra/ap-1",
+ "ra/auto+1",
+ "ra/battle_complete",
+ "ra/battle_exit",
+ "ra/battle_exit_dialog",
+ "ra/click_anywhere",
+ "ra/click_to_continue",
+ "ra/confirm_green",
+ "ra/confirm_red",
+ "ra/continue_button",
+ "ra/cook_button",
+ "ra/day_1",
+ "ra/day_2",
+ "ra/day_3",
+ "ra/day_4",
+ "ra/day_complete",
+ "ra/day_next",
+ "ra/days",
+ "ra/delete_save",
+ "ra/delete_save_confirm_dialog",
+ "ra/delete_save_confirm_dialog_ok_button",
+ "ra/dialog_cancel",
+ "ra/drink_0",
+ "ra/drink_2",
+ "ra/drink_4",
+ "ra/enter_battle_confirm_dialog",
+ "ra/get_item",
+ "ra/guide_dialog",
+ "ra/guide_entrance",
+ "ra/main_title",
+ "ra/map_back",
+ "ra/max",
+ "ra/no_enough_drink",
+ "ra/no_enough_resources",
+ "ra/notice",
+ "ra/out_of_drink",
+ "ra/period_complete",
+ "ra/period_complete_start_new_day",
+ "ra/popup",
+ "ra/prepared_0",
+ "ra/prepared_1",
+ "ra/prepared_2",
+ "ra/return_from_kitchen",
+ "ra/save",
+ "ra/shop",
+ "ra/spring",
+ "ra/squad_back",
+ "ra/squad_edit",
+ "ra/squad_edit_confirm_dialog",
+ "ra/squad_edit_start_button",
+ "ra/start_action",
+ "ra/start_button",
+ "ra/waste_time_button",
+ "ra/waste_time_dialog",
+ "riic/assistants",
+ "riic/exp",
+ "riic/iron",
+ "riic/manufacture",
+ "riic/orundum",
+ "riic/report_title",
+ "riic/trade",
+ "room/1",
+ "room/2",
+ "room/3",
+ "room/4",
+ "room/central",
+ "room/contact",
+ "room/dormitory",
+ "room/meeting",
+ "sf/available",
+ "sf/card",
+ "sf/click_anywhere",
+ "sf/confirm",
+ "sf/continue",
+ "sf/continue_event",
+ "sf/continue_result",
+ "sf/end",
+ "sf/entrance",
+ "sf/exit",
+ "sf/exit_button",
+ "sf/failure",
+ "sf/info",
+ "sf/inheritance",
+ "sf/lost_in_the_trick",
+ "sf/percentage",
+ "sf/properties",
+ "sf/ranger",
+ "sf/restart",
+ "sf/select",
+ "sf/select_team_ok",
+ "sf/success",
+ "sf/support_battle_platform",
+ "sf/team_intelligence",
+ "sf/team_management",
+ "sf/team_medicine",
+ "sf/team_pass",
+ "shop_discount_sold/50",
+ "shop_discount_sold/75",
+ "shop_discount_sold/95",
+ "shop_discount_sold/99",
+ "sss/close_button",
+ "sss/device_button",
+ "sss/ec_button",
+ "sss/loading",
+ "sss/main",
+ "sss/start_button",
+ "sss/deploy_button",
+ "sss/redeploy_button",
+ "sss/squad_button",
+ "recruit/agent_token",
+ "recruit/agent_token_first",
+ "recruit/available_level",
+ "recruit/begin_recruit",
+ "recruit/career_needs",
+ "recruit/job_requirements",
+ "recruit/lmb",
+ "recruit/recruit_done",
+ "recruit/recruit_lock",
+ "recruit/refresh",
+ "recruit/refresh_comfirm",
+ "recruit/start_recruit",
+ "recruit/stone",
+ "recruit/ticket",
+ "recruit/time",
+ "navigation/act/0",
+ "navigation/act/1",
+ "navigation/act/2",
+ "navigation/biography/OF_banner",
+ "navigation/biography/OF_entry",
+ "navigation/collection/AP-1",
+ "navigation/collection/AP_entry",
+ "navigation/collection/AP_not_available",
+ "navigation/collection/CA-1",
+ "navigation/collection/CA_entry",
+ "navigation/collection/CA_not_available",
+ "navigation/collection/CE-1",
+ "navigation/collection/CE_entry",
+ "navigation/collection/CE_not_available",
+ "navigation/collection/LS-1",
+ "navigation/collection/LS_entry",
+ "navigation/collection/PR-A-1",
+ "navigation/collection/PR-A_entry",
+ "navigation/collection/PR-A_not_available",
+ "navigation/collection/PR-B-1",
+ "navigation/collection/PR-B_entry",
+ "navigation/collection/PR-B_not_available",
+ "navigation/collection/PR-C-1",
+ "navigation/collection/PR-C_entry",
+ "navigation/collection/PR-C_not_available",
+ "navigation/collection/PR-D-1",
+ "navigation/collection/PR-D_entry",
+ "navigation/collection/PR-D_not_available",
+ "navigation/collection/SK-1",
+ "navigation/collection/SK_entry",
+ "navigation/collection/SK_not_available",
+ "navigation/main/0",
+ "navigation/main/1",
+ "navigation/main/10",
+ "navigation/main/11",
+ "navigation/main/12",
+ "navigation/main/13",
+ "navigation/main/14",
+ "navigation/main/2",
+ "navigation/main/3",
+ "navigation/main/4",
+ "navigation/main/5",
+ "navigation/main/6",
+ "navigation/main/7",
+ "navigation/main/8",
+ "navigation/main/9",
+ "ra/map/base",
+ "ra/map/冲突区_丢失的订单",
+ "ra/map/后舍_众人会聚之地",
+ "ra/map/奇遇_崎岖窄路",
+ "ra/map/奇遇_砾沙平原",
+ "ra/map/奇遇_风啸峡谷",
+ "ra/map/捕猎区_聚羽之地",
+ "ra/map/要塞_征税的选择",
+ "ra/map/资源区_射程以内",
+ "ra/map/资源区_林中寻宝",
+ "recruit/riic_res/CASTER",
+ "recruit/riic_res/MEDIC",
+ "recruit/riic_res/PIONEER",
+ "recruit/riic_res/SNIPER",
+ "recruit/riic_res/SPECIAL",
+ "recruit/riic_res/SUPPORT",
+ "recruit/riic_res/TANK",
+ "recruit/riic_res/WARRIOR",
+]
diff --git a/arknights_mower/utils/update.py b/arknights_mower/utils/update.py
new file mode 100644
index 000000000..42ccd80ff
--- /dev/null
+++ b/arknights_mower/utils/update.py
@@ -0,0 +1,111 @@
+import os
+import zipfile
+
+import requests
+
+from .. import __version__
+
+
+# 编写bat脚本,删除旧程序,运行新程序
+def __write_restart_cmd(new_name, old_name):
+ b = open("upgrade.bat", "w")
+ TempList = "@echo off\n"
+ TempList += (
+ "if not exist " + new_name + " exit \n"
+ ) # 判断是否有新版本的程序,没有就退出更新。
+ TempList += "echo 正在更新至最新版本...\n"
+ TempList += "timeout /t 5 /nobreak\n" # 等待5秒
+ TempList += (
+ "if exist " + old_name + ' del "' + old_name.replace("/", "\\\\") + '"\n'
+ ) # 删除旧程序
+ TempList += (
+ 'copy "'
+ + new_name.replace("/", "\\\\")
+ + '" "'
+ + old_name.replace("/", "\\\\")
+ + '"\n'
+ ) # 复制新版本程序
+ TempList += "echo 更新完成,正在启动...\n"
+ TempList += "timeout /t 3 /nobreak\n"
+ TempList += "start " + old_name + " \n" # "start 1.bat\n"
+ TempList += "exit"
+ b.write(TempList)
+ b.close()
+ # subprocess.Popen("upgrade.bat") #不显示cmd窗口
+ os.system("start upgrade.bat") # 显示cmd窗口
+ os._exit(0)
+
+
+def compere_version():
+ """
+ 与github上最新版比较
+ :return res: str | None, 若需要更新 返回版本号, 否则返回None
+ """
+ newest_version = __get_newest_version()
+
+ v1 = [str(x) for x in str(__version__).split(".")]
+ v2 = [str(x) for x in str(newest_version).split(".")]
+
+ # 如果2个版本号位数不一致,后面使用0补齐,使2个list长度一致,便于后面做对比
+ if len(v1) > len(v2):
+ v2 += [str(0) for x in range(len(v1) - len(v2))]
+ elif len(v1) < len(v2):
+ v1 += [str(0) for x in range(len(v2) - len(v1))]
+ list_sort = sorted([v1, v2])
+ if list_sort[0] == list_sort[1]:
+ return None
+ elif list_sort[0] == v1:
+ return newest_version
+ else:
+ return None
+
+
+def update_version():
+ if os.path.isfile("upgrade.bat"):
+ os.remove("upgrade.bat")
+ __write_restart_cmd("tmp/mower.exe", "./mower.exe")
+
+
+def __get_newest_version():
+ response = requests.get(
+ "https://api.github.com/repos/ArkMowers/arknights-mower/releases/latest"
+ )
+ return response.json()["tag_name"]
+
+
+def download_version(version):
+ if not os.path.isdir("./tmp"):
+ os.makedirs("./tmp")
+ r = requests.get(
+ f"https://github.com/ArkMowers/arknights-mower/releases/download/{version}/mower.zip",
+ stream=True,
+ )
+ # r = requests.get(
+ # f"https://github.com/ArkMowers/arknights-mower/releases/download/{version}/arknights-mower-3.0.4.zip",
+ # stream=True)
+ total = int(r.headers.get("content-length", 0))
+ index = 0
+ with open("./tmp/mower.zip", "wb") as f:
+ for chunk in r.iter_content(chunk_size=10485760):
+ if chunk:
+ f.write(chunk)
+ index += len(chunk)
+ print(f"更新进度:{'%.2f%%' % (index*100 / total)}({index}/{total})")
+ zip_file = zipfile.ZipFile("./tmp/mower.zip")
+ zip_list = zip_file.namelist()
+
+ for f in zip_list:
+ zip_file.extract(f, "./tmp/")
+ zip_file.close()
+ os.remove("./tmp/mower.zip")
+
+
+def main():
+ # 新程序启动时,删除旧程序制造的脚本
+ if os.path.isfile("upgrade.bat"):
+ os.remove("upgrade.bat")
+ __write_restart_cmd("newVersion.exe", "Version.exe")
+
+
+if __name__ == "__main__":
+ compere_version()
diff --git a/arknights_mower/utils/vector.py b/arknights_mower/utils/vector.py
new file mode 100644
index 000000000..e0e3833ed
--- /dev/null
+++ b/arknights_mower/utils/vector.py
@@ -0,0 +1,16 @@
+from arknights_mower.utils import typealias as tp
+
+
+def va(a: tp.Coordinate, b: tp.Coordinate) -> tp.Coordinate:
+ """向量加法,vector add"""
+ return a[0] + b[0], a[1] + b[1]
+
+
+def vs(a: tp.Coordinate, b: tp.Coordinate) -> tp.Coordinate:
+ """向量减法,vector subtract"""
+ return a[0] - b[0], a[1] - b[1]
+
+
+def sa(scope: tp.Scope, vector: tp.Coordinate) -> tp.Scope:
+ """区域偏移,scope add"""
+ return va(scope[0], vector), va(scope[1], vector)
diff --git a/arknights_mower/utils/yaml.py b/arknights_mower/utils/yaml.py
deleted file mode 100644
index 254a20547..000000000
--- a/arknights_mower/utils/yaml.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import ruamel.yaml
-
-yaml = ruamel.yaml.YAML()
diff --git a/arknights_mower/vendor/droidcast/DroidCast-debug-1.2.1.apk b/arknights_mower/vendor/droidcast/DroidCast-debug-1.2.1.apk
new file mode 100644
index 000000000..edd8f1397
Binary files /dev/null and b/arknights_mower/vendor/droidcast/DroidCast-debug-1.2.1.apk differ
diff --git a/arknights_mower/vendor/maatouch/maatouch b/arknights_mower/vendor/maatouch/maatouch
new file mode 100644
index 000000000..e164cdb89
Binary files /dev/null and b/arknights_mower/vendor/maatouch/maatouch differ
diff --git a/auto_get_res_new.py b/auto_get_res_new.py
new file mode 100644
index 000000000..946fa7340
--- /dev/null
+++ b/auto_get_res_new.py
@@ -0,0 +1,713 @@
+import json
+import lzma
+import os
+import pickle
+import re
+from datetime import datetime
+
+import cv2
+import numpy as np
+from PIL import Image, ImageDraw, ImageFont
+from skimage.feature import hog
+from sklearn.neighbors import KNeighborsClassifier
+
+from arknights_mower.utils.image import loadimg, thres2
+
+
+class Arknights数据处理器:
+ def __init__(self):
+ self.当前时间戳 = datetime.now().timestamp()
+ self.物品表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/item_table.json"
+ )
+ self.干员表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/character_table.json"
+ )
+ self.抽卡表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/gacha_table.json"
+ )
+ self.关卡表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/stage_table.json"
+ )
+ self.活动表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/activity_table.json"
+ )
+ self.基建表 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/building_data.json"
+ )
+ self.游戏变量 = self.加载json(
+ "./ArknightsGameResource/gamedata/excel/gamedata_const.json"
+ )
+ self.装仓库物品的字典 = {"NORMAL": [], "CONSUME": [], "MATERIAL": []}
+
+ self.常驻关卡 = self.加载json("arknights_mower/data/stage_data.json")
+
+ self.所有buff = []
+
+ self.限定十连 = self.抽卡表["limitTenGachaItem"]
+ self.联动十连 = self.抽卡表["linkageTenGachaItem"]
+ self.普通十连 = self.抽卡表["normalGachaItem"]
+ self.所有卡池 = self.限定十连 + self.联动十连 + self.普通十连
+
+ def 加载json(self, file_path):
+ with open(file_path, "r", encoding="utf-8") as f:
+ return json.load(f)
+
+ def 添加物品(self):
+ def 检查图标代码匹配(目标图标代码, 物品类型):
+ 匹配结果 = False
+ for 池子限时物品 in self.所有卡池:
+ if (
+ 池子限时物品["itemId"] == 目标图标代码
+ and self.当前时间戳 > 池子限时物品["endTime"]
+ ):
+ 匹配结果 = True
+ break
+ 分割部分 = 目标图标代码.split("_")
+ if len(分割部分) == 2 and 分割部分[0].endswith("recruitment10"):
+ 匹配结果 = True
+ if len(分割部分) == 6 and int(分割部分[5]) < 2023:
+ 匹配结果 = True
+
+ if len(分割部分) == 3 and 目标图标代码.startswith("uni"):
+ 匹配结果 = True
+
+ if len(分割部分) == 3 and 目标图标代码.startswith("voucher_full"):
+ 匹配结果 = True
+ if 目标图标代码 == "ap_supply_lt_60":
+ 匹配结果 = True
+ 抽卡 = self.抽卡表.get("gachaPoolClient", [])
+ for 卡池 in 抽卡:
+ if 卡池["LMTGSID"] == 目标图标代码 and self.当前时间戳 > int(
+ 卡池["endTime"]
+ ):
+ 匹配结果 = True
+ return 匹配结果
+
+ self.物品_名称对 = {}
+
+ if not os.path.exists("./ui/public/depot/EXP.webp"):
+ png_image = Image.open("./ArknightsGameResource/item/EXP_PLAYER.png")
+ png_image.save("./ui/public/depot/EXP.webp", "WEBP")
+ for 物品代码, 物品数据 in self.物品表["items"].items():
+ 中文名称 = 物品数据.get("name", "")
+ 图标代码 = 物品数据.get("iconId", "")
+ 排序代码 = 物品数据.get("sortId", "")
+
+ 分类类型 = 物品数据.get("classifyType", "")
+ 物品类型 = 物品数据.get("itemType", "")
+
+ 源文件路径 = f"./ArknightsGameResource/item/{图标代码}.png"
+ 排除开关 = False
+ 排除开关 = 检查图标代码匹配(图标代码, 物品类型)
+ if 分类类型 != "NONE" and 排序代码 > 0 and not 排除开关:
+ if os.path.exists(源文件路径):
+ 目标文件路径 = f"./ui/public/depot/{中文名称}.webp"
+ self.装仓库物品的字典[分类类型].append([目标文件路径, 源文件路径])
+ if not os.path.exists(目标文件路径):
+ png_image = Image.open(源文件路径)
+ png_image.save(目标文件路径, "WEBP")
+ templist = [物品代码, 图标代码, 中文名称, 分类类型, 排序代码]
+ self.物品_名称对[物品代码] = templist
+ self.物品_名称对[中文名称] = templist
+ print(f"复制 {源文件路径} 到 {目标文件路径}")
+ else:
+ print(f"可以复制,但是未找到: {源文件路径}")
+ with open(
+ "./arknights_mower/data/key_mapping.json", "w", encoding="utf8"
+ ) as json_file:
+ json.dump(self.物品_名称对, json_file, ensure_ascii=False, indent=4)
+ print()
+
+ def 添加干员(self):
+ 干员_名称列表 = []
+
+ for 干员代码, 干员数据 in self.干员表.items():
+ if not 干员数据["itemObtainApproach"]:
+ continue
+
+ 干员名 = 干员数据["name"]
+ 干员_名称列表.append(干员名)
+ 干员头像路径 = f"./ArknightsGameResource/avatar/{干员代码}.png"
+ 目标路径 = f"./ui/public/avatar/{干员数据['name']}.webp"
+ print(f"{干员名}: {干员代码}")
+
+ png_image = Image.open(干员头像路径)
+ png_image.save(目标路径, "WEBP")
+ 干员_名称列表.sort(key=len)
+ with open("./arknights_mower/data/agent.json", "w", encoding="utf-8") as f:
+ json.dump(干员_名称列表, f, ensure_ascii=False)
+ print()
+
+ def 读取卡池(self):
+ 抽卡 = self.抽卡表.get("gachaPoolClient", [])
+ 卡池类型映射 = {
+ "SINGLE": "单人池",
+ "LIMITED": "限定池",
+ "NORM": "普通池",
+ "CLASSIC": "中坚池",
+ "CLASSIC_ATTAIN": "跨年中坚池",
+ "LINKAGE": "联动池",
+ "ATTAIN": "跨年池",
+ "FESCLASSIC": "中坚甄选",
+ }
+
+ for 项 in 抽卡:
+ 卡池名称 = 项.get("gachaPoolName")
+ 开始时间戳 = 项.get("openTime")
+ 结束时间戳 = 项.get("endTime")
+ 卡池类型代码 = 项.get("gachaPoolId")
+ 卡池出人 = 项.get("dynMeta")
+
+ if self.当前时间戳 < 结束时间戳:
+ 卡池类型 = 卡池类型映射.get(卡池类型代码.split("_")[0], 卡池类型代码)
+ if 卡池类型代码.split("_")[1] == "ATTAIN":
+ 卡池类型 = "跨年中坚池"
+ if 卡池名称 == "适合多种场合的强力干员":
+ 卡池名称 = 卡池类型
+ 开始时间 = datetime.fromtimestamp(开始时间戳)
+ 结束时间 = datetime.fromtimestamp(结束时间戳 + 1)
+ print("卡池名称:", 卡池名称)
+ print("卡池类型:", 卡池类型)
+ if 卡池类型 == "中坚池":
+ print(
+ 卡池出人["main6RarityCharId"],
+ 卡池出人["sub6RarityCharId"],
+ 卡池出人["rare5CharList"],
+ )
+ if self.当前时间戳 > 开始时间戳:
+ print("正在进行")
+ print("卡池结束时间:", 结束时间)
+ else:
+ print("卡池开始时间:", 开始时间)
+ print("卡池结束时间:", 结束时间)
+ print(卡池类型代码)
+ print()
+
+ def 读取活动关卡(self):
+ 关卡 = self.关卡表["stageValidInfo"]
+ 还未结束的非常驻关卡 = {
+ 键: 值
+ for 键, 值 in 关卡.items()
+ if 值["endTs"] != -1 and 值["endTs"] > self.当前时间戳
+ }
+ 还未结束的非常驻关卡 = dict(sorted(还未结束的非常驻关卡.items()))
+ for 键, _ in 还未结束的非常驻关卡.items():
+ 关卡代码 = self.关卡表["stages"][键]["code"]
+ if 键.endswith("#f#"):
+ 关卡代码 += " 突袭"
+ 关卡名称 = self.关卡表["stages"][键]["name"]
+ 关卡结束时间戳 = 还未结束的非常驻关卡[键]["endTs"]
+ # 关卡结束时间 = datetime.fromtimestamp(还未结束的非常驻关卡[键]["endTs"] + 1)
+ 关卡掉落表 = self.关卡表["stages"][键]["stageDropInfo"][
+ "displayDetailRewards"
+ ]
+
+ 关卡掉落 = {}
+ 突袭首次掉落 = [
+ self.物品表.get("items", {}).get(item["id"], {}).get("name", item["id"])
+ for item in 关卡掉落表
+ if item["dropType"] == 1
+ ]
+ 常规掉落 = [
+ self.物品表.get("items", {}).get(item["id"], {}).get("name", item["id"])
+ for item in 关卡掉落表
+ if item["dropType"] == 2
+ ]
+ 特殊掉落 = [
+ self.物品表.get("items", {}).get(item["id"], {}).get("name", item["id"])
+ for item in 关卡掉落表
+ if item["dropType"] == 3
+ ]
+ 额外物资 = [
+ self.物品表.get("items", {}).get(item["id"], {}).get("name", item["id"])
+ for item in 关卡掉落表
+ if item["dropType"] == 4
+ ]
+ 首次掉落 = [
+ self.物品表.get("items", {}).get(item["id"], {}).get("name", item["id"])
+ for item in 关卡掉落表
+ if item["dropType"] == 8
+ ]
+ 关卡掉落 = {
+ "突袭首次掉落": 突袭首次掉落,
+ "常规掉落": 常规掉落,
+ "首次掉落": 首次掉落,
+ "特殊掉落": 特殊掉落,
+ "额外物资": 额外物资,
+ }
+
+ self.常驻关卡.append(
+ {
+ "id": 关卡代码,
+ "name": 关卡名称,
+ "drop": 关卡掉落,
+ "end": 关卡结束时间戳,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ }
+ )
+ unkey = 0
+ for item in self.常驻关卡:
+ item["key"] = unkey
+ unkey += 1
+ with open(
+ "./ui/src/pages/stage_data/event_data.json", "w", encoding="utf-8"
+ ) as f:
+ json.dump(self.常驻关卡, f, ensure_ascii=False, indent=2)
+
+ def load_recruit_data(self):
+ recruit_data = {}
+ recruit_result_data = {
+ 4: [],
+ 3: [],
+ 2: [],
+ 1: [],
+ -1: [],
+ }
+ # for 干员代码, 干员数据 in self.干员表.items():
+ # print(干员代码,干员数据)
+ recruit_list = self.抽卡表["recruitDetail"].replace("\\n<@rc.eml>", "")
+ recruit_list = recruit_list.replace("\\n", "")
+ recruit_list = recruit_list.replace("\r", "")
+ recruit_list = recruit_list.replace("★", "")
+ recruit_list = recruit_list.replace("<@rc.eml>", "")
+ recruit_list = recruit_list.replace(">", "")
+ recruit_list = recruit_list.replace("/", "")
+ recruit_list = recruit_list.replace(" ", "\n")
+ recruit_list = recruit_list.replace("--------------------", "")
+ recruit_list = recruit_list.replace("<@rc.title>公开招募说明", "")
+ recruit_list = recruit_list.replace("<@rc.em>※稀有职业需求招募说明※", "")
+ recruit_list = recruit_list.replace(
+ "<@rc.em>当职业需求包含高级资深干员,且招募时限为9小时时,招募必得6星干员",
+ "",
+ )
+ recruit_list = recruit_list.replace(
+ "<@rc.em>当职业需求包含资深干员同时不包含高级资深干员,且招募时限为9小时,则该次招募必得5星干员",
+ "",
+ )
+ recruit_list = recruit_list.replace("<@rc.subtitle>※全部可能出现的干员※", "")
+ recruit_list = recruit_list.replace("绿色高亮的不可寻访干员,可以在此招募", "")
+ recruit_list = recruit_list.split("\n")
+
+ profession = {
+ "MEDIC": "医疗干员",
+ "WARRIOR": "近卫干员",
+ "SPECIAL": "特种干员",
+ "SNIPER": "狙击干员",
+ "CASTER": "术师干员",
+ "TANK": "重装干员",
+ "SUPPORT": "辅助干员",
+ "PIONEER": "先锋干员",
+ }
+
+ for 干员代码, 干员数据 in self.干员表.items():
+ 干员名 = 干员数据["name"]
+
+ if 干员数据["profession"] not in profession:
+ continue
+
+ if 干员名 in recruit_list:
+ tag = 干员数据["tagList"]
+ # 数据中稀有度从0-5
+ 干员数据["rarity"] = 干员数据["rarity"] + 1
+ if len(干员名) <= 4:
+ recruit_result_data[len(干员名)].append(干员代码)
+ else:
+ recruit_result_data[-1].append(干员代码)
+ if 干员数据["rarity"] == 5:
+ tag.append("资深干员")
+ elif 干员数据["rarity"] == 6:
+ tag.append("高级资深干员")
+
+ if 干员数据["position"] == "MELEE":
+ tag.append("近战位")
+ elif 干员数据["position"] == "RANGED":
+ tag.append("远程位")
+
+ tag.append(profession[干员数据["profession"]])
+
+ recruit_data[干员代码] = {
+ "name": 干员名,
+ "stars": 干员数据["rarity"],
+ "tags": 干员数据["tagList"],
+ }
+ print(
+ "{} stars:{} tags:{}".format(
+ 干员名, 干员数据["rarity"], 干员数据["tagList"]
+ )
+ )
+ print("载入公招干员数据{}个".format(len(recruit_data)))
+ with open("./arknights_mower/data/recruit.json", "w", encoding="utf-8") as f:
+ json.dump(recruit_data, f, ensure_ascii=False, indent=4)
+
+ with open(
+ "./arknights_mower/data/recruit_result.json", "w", encoding="utf-8"
+ ) as f:
+ json.dump(recruit_result_data, f, ensure_ascii=False, indent=4)
+
+ def load_recruit_template(self):
+ # !/usr/bin/env python3
+ template = {}
+ with open("./arknights_mower/data/recruit.json", "r", encoding="utf-8") as f:
+ recruit_operators = json.load(f)
+
+ font = ImageFont.truetype("FZDYSK.TTF", 120)
+ print(len(recruit_operators))
+ for operator in recruit_operators:
+ im = Image.new(mode="RGBA", size=(1920, 1080))
+ draw = ImageDraw.Draw(im)
+ draw.text((0, 0), recruit_operators[operator]["name"], font=font)
+ im = im.crop(im.getbbox())
+ im = cv2.cvtColor(np.asarray(im), cv2.COLOR_RGB2GRAY)
+ template[operator] = im
+
+ with lzma.open("arknights_mower/models/recruit_result.pkl", "wb") as f:
+ pickle.dump(template, f)
+
+ def load_recruit_tag(self):
+ with open("./arknights_mower/data/recruit.json", "r", encoding="utf-8") as f:
+ recruit_agent = json.load(f)
+
+ font = ImageFont.truetype(
+ "arknights_mower/fonts/SourceHanSansCN-Medium.otf", 30
+ )
+ recruit_tag = ["资深干员", "高级资深干员"]
+ recruit_tag_template = {}
+ for x in recruit_agent.values():
+ recruit_tag += x["tags"]
+ recruit_tag = list(set(recruit_tag))
+ for tag in recruit_tag:
+ im = Image.new(mode="RGBA", color=(49, 49, 49), size=(215, 70))
+ W, H = im.size
+ draw = ImageDraw.Draw(im)
+ _, _, w, h = draw.textbbox((0, 0), tag, font=font)
+ draw.text(((W - w) / 2, (H - h) / 2 - 5), tag, font=font)
+ recruit_tag_template[tag] = cv2.cvtColor(
+ np.array(im.crop(im.getbbox())), cv2.COLOR_RGB2BGR
+ )
+ with lzma.open("./arknights_mower/models/recruit.pkl", "wb") as f:
+ pickle.dump(recruit_tag_template, f)
+
+ def load_recruit_resource(self):
+ self.load_recruit_data()
+ self.load_recruit_template()
+ self.load_recruit_tag()
+
+ def 训练仓库的knn模型(self, 模板文件夹, 模型保存路径):
+ def 提取特征点(模板):
+ 模板 = 模板[40:173, 40:173]
+ hog_features = hog(
+ 模板,
+ orientations=18,
+ pixels_per_cell=(8, 8),
+ cells_per_block=(2, 2),
+ block_norm="L2-Hys",
+ transform_sqrt=True,
+ channel_axis=2,
+ )
+
+ return hog_features
+
+ def 加载图片特征点_标签(模板类型):
+ 特征点列表 = []
+ 标签列表 = []
+ for [目标文件路径, 源文件路径] in self.装仓库物品的字典[模板类型]:
+ 模板 = cv2.imread(源文件路径)
+ 模板 = cv2.resize(模板, (213, 213))
+ 特征点 = 提取特征点(模板)
+ 特征点列表.append(特征点)
+ 标签列表.append(self.物品_名称对[目标文件路径[18:-5]][2])
+ return 特征点列表, 标签列表
+
+ def 训练knn模型(images, labels):
+ knn_classifier = KNeighborsClassifier(
+ weights="distance", n_neighbors=1, n_jobs=-1
+ )
+ knn_classifier.fit(images, labels)
+ return knn_classifier
+
+ def 保存knn模型(classifier, filename):
+ with lzma.open(filename, "wb") as f:
+ pickle.dump(classifier, f)
+
+ 模板特征点, 模板标签 = 加载图片特征点_标签(模板文件夹)
+ knn模型 = 训练knn模型(模板特征点, 模板标签)
+ 保存knn模型(knn模型, 模型保存路径)
+
+ def 批量训练并保存扫仓库模型(self):
+ self.训练仓库的knn模型("NORMAL", "./arknights_mower/models/NORMAL.pkl")
+ self.训练仓库的knn模型("CONSUME", "./arknights_mower/models/CONSUME.pkl")
+ # self.训练仓库的knn模型("MATERIAL", "./arknights_mower/models/MATERIAL.pkl")
+
+ def 训练在房间内的干员名的模型(self):
+ font = ImageFont.truetype(
+ "arknights_mower/fonts/SourceHanSansCN-Medium.otf", 37
+ )
+
+ data = {}
+
+ kernel = np.ones((12, 12), np.uint8)
+
+ with open("./arknights_mower/data/agent.json", "r", encoding="utf-8") as f:
+ agent_list = json.load(f)
+ for operator in sorted(agent_list, key=lambda x: len(x), reverse=True):
+ img = Image.new(mode="L", size=(400, 100))
+ draw = ImageDraw.Draw(img)
+ draw.text((50, 20), operator, fill=(255,), font=font)
+ img = np.array(img, dtype=np.uint8)
+ img = thres2(img, 200)
+ dilation = cv2.dilate(img, kernel, iterations=1)
+ contours, _ = cv2.findContours(
+ dilation, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
+ )
+ rect = map(lambda c: cv2.boundingRect(c), contours)
+ x, y, w, h = sorted(rect, key=lambda c: c[0])[0]
+ img = img[y : y + h, x : x + w]
+ tpl = np.zeros((46, 265), dtype=np.uint8)
+ tpl[: img.shape[0], : img.shape[1]] = img
+ # cv2.imwrite(f"/home/zhao/Desktop/data/{operator}.png", tpl)
+ data[operator] = tpl
+
+ with lzma.open("arknights_mower/models/operator_room.model", "wb") as f:
+ pickle.dump(data, f)
+
+ def 训练选中的干员名的模型(self):
+ font31 = ImageFont.truetype(
+ "arknights_mower/fonts/SourceHanSansCN-Medium.otf", 31
+ )
+ font30 = ImageFont.truetype(
+ "arknights_mower/fonts/SourceHanSansCN-Medium.otf", 30
+ )
+ font25 = ImageFont.truetype(
+ "arknights_mower/fonts/SourceHanSansCN-Medium.otf", 25
+ )
+
+ data = {}
+
+ kernel = np.ones((10, 10), np.uint8)
+
+ with open("./arknights_mower/data/agent.json", "r", encoding="utf-8") as f:
+ agent_list = json.load(f)
+ for idx, operator in enumerate(agent_list):
+ font = font31
+ if not operator[0].encode().isalpha():
+ if len(operator) == 7:
+ font = font25
+ elif len(operator) == 6:
+ font = font30
+ img = Image.new(mode="L", size=(400, 100))
+ draw = ImageDraw.Draw(img)
+ draw.text((50, 20), operator, fill=(255,), font=font)
+ img = np.array(img, dtype=np.uint8)
+ img = thres2(img, 140)
+ dilation = cv2.dilate(img, kernel, iterations=1)
+ contours, _ = cv2.findContours(
+ dilation, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
+ )
+ rect = map(lambda c: cv2.boundingRect(c), contours)
+ x, y, w, h = sorted(rect, key=lambda c: c[0])[0]
+ img = img[y : y + h, x : x + w]
+ tpl = np.zeros((42, 200), dtype=np.uint8)
+ tpl[: img.shape[0], : img.shape[1]] = img
+ # cv2.imwrite(f"/home/zhao/Desktop/data/{operator}.png", tpl)
+ data[operator] = tpl
+
+ with lzma.open("arknights_mower/models/operator_select.model", "wb") as f:
+ pickle.dump(data, f)
+
+ def auto_fight_avatar(self):
+ avatar_mapping = {} # char_285_medic2 -> Lancet-2
+ for name, data in self.干员表.items():
+ avatar_mapping[name] = data["name"]
+ avatar = {} # Lancet-2 -> List[avatar image]
+ avatar_path = "./ArknightsGameResource/avatar"
+ for i in os.listdir(avatar_path):
+ # i: char_285_medic2.png
+ for j, k in avatar_mapping.items():
+ # j: char_285_medic2
+ # k: Lancet-2
+ if i.startswith(j):
+ img = loadimg(os.path.join(avatar_path, i), True)
+ img = cv2.resize(img, None, None, 0.5, 0.5)
+ if k not in avatar:
+ avatar[k] = []
+ avatar[k].append(img)
+ break
+ with lzma.open("./arknights_mower/models/avatar.pkl", "wb") as f:
+ pickle.dump(avatar, f)
+
+ def 获得干员基建描述(self):
+ buff描述 = self.基建表["buffs"]
+ buff_table = {}
+ for buff名称, 相关buff in buff描述.items():
+ buff_table[buff名称] = [
+ 相关buff["buffName"],
+ 相关buff["description"],
+ 相关buff["roomType"],
+ 相关buff["buffCategory"],
+ 相关buff["skillIcon"],
+ 相关buff["buffColor"],
+ 相关buff["textColor"],
+ ]
+
+ 干员技能列表 = []
+
+ name_key = 0
+ for 角色id, 相关buff in self.基建表["chars"].items():
+ 干员技能字典 = {
+ "key": 0,
+ "name": "",
+ "span": 0,
+ "child_skill": [],
+ }
+
+ 干员技能字典["name"] = self.干员表[角色id]["name"]
+
+ skill_key = 0
+ name_key += 1
+ 干员技能字典["key"] = name_key
+ for item in 相关buff["buffChar"]:
+ skill_level = 0
+
+ if item["buffData"] != []:
+ for item2 in item["buffData"]:
+ 干员技能详情 = {}
+
+ 干员技能详情["skill_key"] = skill_key
+ 干员技能详情["skill_level"] = skill_level
+ skill_level += 1
+ 干员技能详情["phase_level"] = (
+ f'精{item2["cond"]["phase"]} {item2["cond"]["level"]}级'
+ )
+ 干员技能详情["skillname"] = buff_table[item2["buffId"]][0]
+ text = buff_table[item2["buffId"]][1]
+ pattern = r"<\$(.*?)>"
+ matches = re.findall(pattern, text)
+ ex_string = []
+ 干员技能详情["buffer"] = False
+ 干员技能详情["buffer_des"] = []
+ if matches:
+ 干员技能详情["buffer"] = True
+ ex_string = list(
+ set([match.replace(".", "_") for match in matches])
+ )
+ ex_string.sort()
+ 干员技能详情["buffer_des"] = ex_string
+ self.所有buff.extend(ex_string)
+
+ 干员技能详情["des"] = text
+ 干员技能详情["roomType"] = roomType[
+ buff_table[item2["buffId"]][2]
+ ]
+ 干员技能详情["buffCategory"] = buff_table[item2["buffId"]][3]
+ 干员技能详情["skillIcon"] = buff_table[item2["buffId"]][4]
+ 干员技能详情["buffColor"] = buff_table[item2["buffId"]][5]
+ 干员技能详情["textColor"] = buff_table[item2["buffId"]][6]
+
+ 干员技能字典["child_skill"].append(干员技能详情)
+
+ 干员技能详情 = []
+ 干员技能字典["span"] = len(干员技能字典["child_skill"])
+ skill_key += 1
+ 干员技能列表.append(干员技能字典.copy())
+ 干员技能列表 = sorted(干员技能列表, key=lambda x: (-x["key"]))
+ # print(干员技能列表)
+ with open(
+ "./ui/src/pages/basement_skill/skill.json", "w", encoding="utf-8"
+ ) as f:
+ json.dump(干员技能列表, f, ensure_ascii=False, indent=2)
+
+ def buff转换(self):
+ buff_table = {}
+ pattern = r"<\$(.*?)>"
+
+ for item in self.游戏变量["termDescriptionDict"]:
+ matches = re.findall(
+ pattern, self.游戏变量["termDescriptionDict"][item]["description"]
+ )
+ matches = [match.replace(".", "_") for match in matches]
+ dict1 = self.游戏变量["termDescriptionDict"][item]
+ dict1["buffer"] = []
+ if item.startswith("cc") and matches:
+ dict1["buffer"] = matches
+ buff_table[item.replace(".", "_")] = dict1
+
+ with open(
+ "./ui/src/pages/basement_skill/buffer.json", "w", encoding="utf-8"
+ ) as f:
+ json.dump(buff_table, f, ensure_ascii=False, indent=2)
+
+ def 添加基建技能图标(self):
+ # 源目录和目标目录
+ source_dir = "./ArknightsGameResource/building_skill"
+ destination_dir = "./ui/public/building_skill"
+
+ # 创建目标目录(如果不存在)
+ os.makedirs(destination_dir, exist_ok=True)
+ # 遍历源目录中的所有文件
+ for root, dirs, files in os.walk(source_dir):
+ for file in files:
+ if file.endswith(".png"):
+ src_file_path = os.path.join(root, file)
+ # 修改文件扩展名为 .webp
+ dest_file_name = os.path.splitext(file)[0] + ".webp"
+ dest_file_path = os.path.join(destination_dir, dest_file_name)
+ if not os.path.exists(dest_file_path):
+ with Image.open(src_file_path) as img:
+ img.save(dest_file_path, "webp")
+ print(f"转换: {src_file_path} 到 {dest_file_path}")
+ else:
+ print(f"跳过: {dest_file_path} 已存在")
+
+
+roomType = {
+ "POWER": "发电站",
+ "DORMITORY": "宿舍",
+ "MANUFACTURE": "制造站",
+ "MEETING": "会客室",
+ "WORKSHOP": "加工站",
+ "TRADING": "贸易站",
+ "HIRE": "人力办公室",
+ "TRAINING": "训练室",
+ "CONTROL": "中枢",
+}
+
+
+数据处理器 = Arknights数据处理器()
+
+
+数据处理器.添加物品() # 显示在仓库里的物品
+
+数据处理器.添加干员()
+
+数据处理器.读取卡池()
+
+数据处理器.读取活动关卡()
+
+# 和 数据处理器.添加物品() 有联动 , 添加物品提供了分类的图片位置
+数据处理器.批量训练并保存扫仓库模型()
+print("批量训练并保存扫仓库模型,完成")
+
+
+数据处理器.训练在房间内的干员名的模型()
+print("训练在房间内的干员名的模型,完成")
+
+数据处理器.训练选中的干员名的模型()
+print("训练选中的干员名的模型,完成")
+
+
+数据处理器.auto_fight_avatar()
+
+数据处理器.获得干员基建描述()
+
+数据处理器.buff转换() # 所有buff描述,包括其他buff
+
+数据处理器.添加基建技能图标()
+
+数据处理器.load_recruit_resource()
diff --git a/data_update.py b/data_update.py
deleted file mode 100644
index 662ce0d80..000000000
--- a/data_update.py
+++ /dev/null
@@ -1,124 +0,0 @@
-import json
-import os
-
-import fontforge
-import requests
-
-proxies = {'http': 'http://localhost:11223'}
-datadir = 'arknights_mower/data/'
-GitHubURL = 'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/zh_CN/gamedata/'
-
-
-def dump(data, filename):
- with open(datadir + filename, 'w') as f:
- json.dump(data, f, ensure_ascii=False, indent=4, default=str)
-
-
-def requests_get(path):
- return requests.get(GitHubURL + path, proxies=proxies).text
-
-
-agent = []
-character_table = json.loads(requests_get('excel/character_table.json'))
-for x in character_table.values():
- if x['displayNumber'] is not None:
- agent.append(x['name'].strip())
-dump(agent, 'agent.json')
-
-agent_charset = set(''.join(agent))
-Chinese, unChinese = [], []
-for c in agent_charset:
- if ord(c) < 256:
- unChinese.append(c)
- else:
- Chinese.append(c)
-with open('build/Chinese.txt', 'w') as f:
- f.write(''.join(Chinese))
-with open('build/unChinese.txt', 'w') as f:
- f.write(''.join(unChinese))
-
-command = 'java.exe -jar FontPruner/sfnttool.jar -c build/Chinese.txt build/unChinese.txt FontPruner/SourceHanSansSC-Bold.ttf build/SourceHanSansSC-Bold.ttf'
-if os.system(command) is False:
- raise Exception('build new font error!' + command)
-
-
-ttf_file = 'build/SourceHanSansSC-Bold.ttf'
-otf_file = 'arknights_mower/fonts/SourceHanSansSC-Bold.otf'
-
-font = fontforge.open(ttf_file)
-font.generate(otf_file)
-font.close()
-
-
-chapter = []
-chapter_table = json.loads(requests_get('excel/chapter_table.json'))
-for x in chapter_table.values():
- chapter.append(x['chapterName2'])
-dump(chapter, 'chapter.json')
-
-
-level = {}
-zone = {}
-
-zone_table = json.loads(requests_get('excel/zone_table.json'))
-chapterIndex = -1
-for x in zone_table['zones'].values():
- if x['type'] == 'MAINLINE':
- if x['zoneIndex'] == 0:
- chapterIndex += 1
- zone[x['zoneID']] = {
- 'type': x['type'],
- 'name': x['zoneNameSecond'],
- 'chapterIndex': chapterIndex,
- 'zoneIndex': int(x['zoneID'].split('_')[1]),
- }
- elif x['type'] == 'WEEKLY':
- zone[x['zoneID']] = {
- 'type': x['type'],
- 'name': x['zoneNameSecond'],
- 'chapterIndex': None,
- 'zoneIndex': None,
- }
-
-stage_table = json.loads(requests_get('excel/stage_table.json'))
-for x in stage_table['stages'].values():
- if (
- x['zoneId'] in zone.keys()
- and x['canBattleReplay']
- and not x['levelId'].startswith('Activities')
- ):
- level[x['code']] = {
- 'zone_id': x['zoneId'],
- 'ap_cost': x['apCost'],
- 'code': x['code'],
- 'name': x['name'],
- }
-
-retro_table = json.loads(requests_get('excel/retro_table.json'))
-for x in retro_table['retroActList'].values():
- if x['type'] == 1:
- zone[x['retroId']] = {
- 'type': 'BRANCHLINE',
- 'name': x['name'],
- 'chapterIndex': None,
- 'zoneIndex': x['index'],
- }
- elif x['type'] == 0:
- zone[x['retroId']] = {
- 'type': 'SIDESTORY',
- 'name': x['name'],
- 'chapterIndex': None,
- 'zoneIndex': x['index'],
- }
-zoneToRetro = retro_table['zoneToRetro']
-for x in retro_table['stageList'].values():
- if x['hardStagedId'] is None and x['canBattleReplay'] and x['zoneId'].endswith('1') and x['zoneId'] in zoneToRetro.keys():
- level[x['code']] = {
- 'zone_id': zoneToRetro[x['zoneId']],
- 'ap_cost': x['apCost'],
- 'code': x['code'],
- 'name': x['name'],
- }
-
-dump(zone, 'zone.json')
-dump(level, 'level.json')
diff --git a/diy.py b/diy.py
index 84e1ebe29..9ec8c3ce8 100644
--- a/diy.py
+++ b/diy.py
@@ -1,285 +1,168 @@
import time
-from datetime import datetime
-from arknights_mower.solvers.base_schedule import BaseSchedulerSolver
+import schedule
+
from arknights_mower.strategy import Solver
-from arknights_mower.utils.device import Device
-from arknights_mower.utils.log import logger, init_fhlr
from arknights_mower.utils import config
-
-
-email_config= {
- 'account':"xxx@qq.com",
- 'pass_code':'从QQ邮箱帐户设置—>生成授权码',
- 'receipts':['任何邮箱'],
- 'notify':False
-}
-maa_config = {
- # 请设置为存放 dll 文件及资源的路径
- "maa_path":'F:\\MAA-v4.10.5-win-x64',
- # 请设置为存放 dll 文件及资源的路径
- "maa_adb_path":"D:\\Program Files\\Nox\\bin\\adb.exe",
- # adb 地址
- "maa_adb":['127.0.0.1:62001'],
- # maa 运行的时间间隔,以小时计
- "maa_execution_gap":4,
- # 以下配置,第一个设置为true的首先生效
- # 是否启动肉鸽
- "roguelike":False,
- # 是否启动生息演算
- "reclamation_algorithm":False,
- # 是否启动保全派驻
- "stationary_security_service":False,
- "last_execution": None,
- "weekly_plan":[{"weekday":"周一","stage":['AP-5'],"medicine":0},
- {"weekday":"周二","stage":['CE-6'],"medicine":0},
- {"weekday":"周三","stage":['1-7'],"medicine":0},
- {"weekday":"周四","stage":['AP-5'],"medicine":0},
- {"weekday":"周五","stage":['1-7'],"medicine":0},
- {"weekday":"周六","stage":['AP-5'],"medicine":0},
- {"weekday":"周日","stage":['AP-5'],"medicine":0}]
-}
-
-# Free (宿舍填充)干员安排黑名单
-free_blacklist= []
-
-# 干员宿舍回复阈值
- # 高效组心情低于 UpperLimit * 阈值 (向下取整)的时候才会会安排休息
- # UpperLimit:默认24,特殊技能干员如夕,令可能会有所不同(设置在 agent-base.json 文件可以自行更改)
-resting_treshhold = 0.5
-
-# 全自动基建排班计划:
-# 这里定义了一套全自动基建的排班计划 plan_1
-# agent 为常驻高效组的干员名
-
-# group 为干员编队,你希望任何编队的人一起上下班则给他们编一样的名字
- # 编队最大数不支持超过4个干员 否则可能会在计算自动排班的时候报错
-# replacement 为替换组干员备选
- # 暖机干员的自动换班
- # 目前只支持一个暖机干员休息
- # !! 会吧其他正在休息的暖机干员赶出宿舍
- # 请尽量安排多的替换干员,且尽量不同干员的替换人员不冲突
- # 龙舌兰和但书默认为插拔干员 必须放在 replacement的第一位
- # 请把你所安排的替换组 写入replacement 否则程序可能报错
- # 替换组会按照从左到右的优先级选择可以编排的干员
- # 宿舍常驻干员不会被替换所以不需要设置替换组
- # 宿舍空余位置请编写为Free,请至少安排一个群补和一个单补 以达到最大恢复效率
- # 宿管必须安排靠左,后面为填充干员
- # 宿舍恢复速率务必1-4从高到低排列
- # 如果有菲亚梅塔则需要安排replacement 建议干员至少为三
- # 菲亚梅塔会从replacment里找最低心情的进行充能
+from arknights_mower.utils.log import init_fhlr, logger
+
+# 指定无人机加速第三层第三个房间的制造或贸易订单
+drone_room = "room_3_3"
+
+# 指定使用菲亚梅塔恢复第一层第二个房间心情最差的干员的心情
+# 恢复后回到原工作岗位,工作顺序不变,以保证最大效率
+fia_room = "room_1_2"
+
+# 指定关卡序列的作战计划
+ope_lists = [["AP-5", 1], ["1-7", -1]]
+
+# 使用信用点购买东西的优先级(从高到低)
+shop_priority = [
+ "招聘许可",
+ "赤金",
+ "龙门币",
+ "初级作战记录",
+ "技巧概要·卷2",
+ "基础作战记录",
+ "技巧概要·卷1",
+]
+
+# 公招选取标签时优先选择的干员的优先级(从高到低)
+recruit_priority = ["因陀罗", "火神"]
+
+# 自定义基建排班
+# 这里自定义了一套排班策略,实现的是两班倒,分为四个阶段
+# 阶段 1 和 2 为第一班,阶段 3 和 4 为第二班
+# 第一班的干员在阶段 3 和 4 分两批休息,第二班同理
+# 每个阶段耗时 6 小时
plan = {
# 阶段 1
- "default": "plan_1",
"plan_1": {
- # 中枢
- 'central': [{'agent': '焰尾', 'group': '红松骑士', 'replacement': ["凯尔希","诗怀雅"]},
- {'agent': '琴柳', 'group': '', 'replacement': ["凯尔希","阿米娅"]},
- {'agent': '重岳', 'group': '夕', 'replacement': ["玛恩纳", "清道夫", "凯尔希", "阿米娅", '坚雷']},
- {'agent': '夕', 'group': '夕', 'replacement': ["玛恩纳", "清道夫", "凯尔希", "阿米娅", '坚雷']},
- {'agent': '令', 'group': '夕', 'replacement': ["玛恩纳", "清道夫", "凯尔希", "阿米娅", '坚雷']},
- ],
- 'contact': [{'agent': '絮雨', 'group': '絮雨', 'replacement': []}],
+ # 控制中枢
+ "central": ["夕", "令", "凯尔希", "阿米娅", "玛恩纳"],
+ # 办公室
+ "contact": ["艾雅法拉"],
# 宿舍
- 'dormitory_1': [{'agent': '流明', 'group': '', 'replacement': []},
- {'agent': '闪灵', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []}
- ],
- 'dormitory_2': [{'agent': '杜林', 'group': '', 'replacement': []},
- {'agent': '蜜莓', 'group': '', 'replacement': []},
- {'agent': '褐果', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []}
- ],
- 'dormitory_3': [{'agent': '车尔尼', 'group': '', 'replacement': []},
- {'agent': '斥罪', 'group': '', 'replacement': []},
- {'agent': '爱丽丝', 'group': '', 'replacement': []},
- {'agent': '桃金娘', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []}
- ],
- 'dormitory_4': [{'agent': '波登可', 'group': '', 'replacement': []},
- {'agent': '夜莺', 'group': '', 'replacement': []},
- {'agent': '菲亚梅塔', 'group': '', 'replacement': ['迷迭香', '黑键', '絮雨','至简']},
- {'agent': 'Free', 'group': '', 'replacement': []},
- {'agent': 'Free', 'group': '', 'replacement': []}],
- 'factory':[{'agent': '年', 'replacement': ['九色鹿','芳汀'], 'group': '夕'}],
+ "dormitory_1": ["杜林", "闪灵", "安比尔", "空弦", "缠丸"],
+ "dormitory_2": ["推进之王", "琴柳", "赫默", "杰西卡", "调香师"],
+ "dormitory_3": ["夜莺", "波登可", "夜刀", "古米", "空爆"],
+ "dormitory_4": ["空", "Lancet-2", "香草", "史都华德", "刻俄柏"],
# 会客室
- 'meeting': [{'agent': '陈', 'replacement': ['星极','远山'], 'group': ''},
- {'agent': '红', 'replacement': ['远山','星极'], 'group': ''} ],
- 'room_1_1': [{'agent': '黑键', 'group': '', 'replacement': []},
- {'agent': '乌有', 'group': '夕', 'replacement': ['但书','图耶']},
- {'agent': '空弦', 'group': '夕', 'replacement': ['龙舌兰', '鸿雪']}
- # {'agent': '伺夜', 'group': '图耶', 'replacement': ['但书','能天使']},
- # {'agent': '空弦', 'group': '图耶', 'replacement': ['龙舌兰', '雪雉']}
- ],
- 'room_1_2': [{'agent': '迷迭香', 'group': '', 'replacement': []},
- {'agent': '砾', 'group': '', 'Type': '', 'replacement': ['斑点','夜烟']},
- {'agent': '至简', 'group': '', 'replacement': []}],
- 'room_1_3': [{'agent': '承曦格雷伊', 'group': '异客', 'replacement': ['炎狱炎熔','格雷伊']}],
- 'room_2_2': [{'agent': '温蒂', 'group': '异客', 'replacement': ['火神']},
- # {'agent': '异客', 'group': '异客', 'Type': '', 'replacement': ['贝娜']},
- {'agent': '异客', 'group': '异客', 'Type': '', 'replacement': ['贝娜']},
- {'agent': '森蚺', 'group': '异客', 'replacement': ['泡泡']}],
- 'room_3_1': [{'agent': '稀音', 'group': '稀音', 'replacement': ['贝娜']},
- {'agent': '帕拉斯', 'group': '稀音', 'Type': '', 'replacement': ['泡泡']},
- {'agent': '红云', 'group': '稀音', 'replacement': ['火神']}],
- 'room_2_3': [{'agent': '澄闪', 'group': '', 'replacement': ['炎狱炎熔', '格雷伊']}],
- 'room_2_1': [{'agent': '食铁兽', 'group': '食铁兽', 'replacement': ['泡泡']},
- {'agent': '断罪者', 'group': '食铁兽', 'replacement': ['火神']},
- {'agent': '槐琥', 'group': '食铁兽', 'replacement': ['贝娜']}],
- 'room_3_2': [{'agent': '灰毫', 'group': '红松骑士', 'replacement': ['贝娜']},
- {'agent': '远牙', 'group': '红松骑士', 'Type': '', 'replacement': ['泡泡']},
- {'agent': '野鬃', 'group': '红松骑士', 'replacement': ['火神']}],
- 'room_3_3': [{'agent': '雷蛇', 'group': '', 'replacement': ['炎狱炎熔','格雷伊']}]
- }
-}
-
-agent_base_config = {
- "Default":{"UpperLimit": 24,"LowerLimit": 0,"ExhaustRequire": False,"ArrangeOrder":[2,"false"],"RestInFull": False},
- "令":{"ArrangeOrder":[2,"true"]},
- "夕": {"ArrangeOrder":[2,"true"]},
- "稀音":{"ExhaustRequire": True,"ArrangeOrder":[2,"true"],"RestInFull": True},
- "巫恋":{"ArrangeOrder":[2,"true"]},
- "柏喙":{"ExhaustRequire": True,"ArrangeOrder":[2,"true"]},
- "龙舌兰":{"ArrangeOrder":[2,"true"]},
- "空弦":{"ArrangeOrder":[2,"true"],"RestingPriority": "low"},
- "伺夜":{"ArrangeOrder":[2,"true"]},
- "绮良":{"ArrangeOrder":[2,"true"]},
- "但书":{"ArrangeOrder":[2,"true"]},
- "泡泡":{"ArrangeOrder":[2,"true"]},
- "火神":{"ArrangeOrder":[2,"true"]},
- "黑键":{"ArrangeOrder":[2,"true"]},
- "波登可":{"ArrangeOrder":[ 2, "false" ]},
- "夜莺":{"ArrangeOrder":[ 2, "false" ]},
- "菲亚梅塔":{"ArrangeOrder":[ 2, "false" ]},
- "流明":{"ArrangeOrder":[ 2, "false" ]},
- "蜜莓":{"ArrangeOrder":[ 2, "false" ]},
- "闪灵":{"ArrangeOrder":[ 2, "false" ]},
- "杜林":{"ArrangeOrder":[ 2, "false" ]},
- "褐果":{"ArrangeOrder":[ 2, "false" ]},
- "车尔尼":{"ArrangeOrder":[ 2, "false" ]},
- "安比尔":{"ArrangeOrder":[ 2, "false" ]},
- "爱丽丝":{"ArrangeOrder":[ 2, "false" ]},
- "桃金娘":{"ArrangeOrder":[ 2, "false" ]},
- "帕拉斯": {"RestingPriority": "low"},
- "红云": {"RestingPriority": "low","ArrangeOrder":[2,"true"]},
- "承曦格雷伊": {"ArrangeOrder":[2,"true"]},
- "乌有":{"ArrangeOrder":[2,"true"],"RestingPriority": "low"},
- "图耶":{"ArrangeOrder":[2,"true"]},
- "鸿雪": {"ArrangeOrder":[2,"true"]},
- "孑":{"ArrangeOrder":[2,"true"]},
- "清道夫":{"ArrangeOrder":[2,"true"]},
- "临光":{"ArrangeOrder":[2,"true"]},
- "杜宾":{"ArrangeOrder":[2,"true"]},
- "焰尾":{"RestInFull": True},
- "重岳":{"ArrangeOrder":[2,"true"]},
- "坚雷":{"ArrangeOrder":[2,"true"]},
- "年":{"RestingPriority": "low"}
+ "meeting": ["陈", "红"],
+ # 制造站 + 贸易站 + 发电站
+ "room_1_1": ["德克萨斯", "能天使", "拉普兰德"],
+ "room_1_2": ["断罪者", "食铁兽", "槐琥"],
+ "room_1_3": ["阿消"],
+ "room_2_1": ["巫恋", "柏喙", "慕斯"],
+ "room_2_2": ["红豆", "霜叶", "白雪"],
+ "room_2_3": ["雷蛇"],
+ "room_3_1": ["Castle-3", "梅尔", "白面鸮"],
+ "room_3_2": ["格雷伊"],
+ "room_3_3": ["砾", "夜烟", "斑点"],
+ },
+ # 阶段 2
+ "plan_2": {
+ # 注释掉了部分和阶段 1 一样排班计划的房间,加快排班速度
+ # 'contact': ['艾雅法拉'],
+ "dormitory_1": ["杜林", "闪灵", "芬", "稀音", "克洛丝"],
+ "dormitory_2": ["推进之王", "琴柳", "清流", "森蚺", "温蒂"],
+ "dormitory_3": ["夜莺", "波登可", "伊芙利特", "深靛", "炎熔"],
+ "dormitory_4": ["空", "Lancet-2", "远山", "星极", "普罗旺斯"],
+ # 'meeting': ['陈', '红'],
+ # 'room_1_1': ['德克萨斯', '能天使', '拉普兰德'],
+ # 'room_1_2': ['断罪者', '食铁兽', '槐琥'],
+ # 'room_1_3': ['阿消'],
+ # 'room_2_1': ['巫恋', '柏喙', '慕斯'],
+ # 'room_2_2': ['红豆', '霜叶', '白雪'],
+ # 'room_2_3': ['雷蛇'],
+ # 'room_3_1': ['Castle-3', '梅尔', '白面鸮'],
+ # 'room_3_2': ['格雷伊'],
+ # 'room_3_3': ['砾', '夜烟', '斑点']
+ },
+ "plan_3": {
+ "contact": ["普罗旺斯"],
+ "dormitory_1": ["杜林", "闪灵", "格雷伊", "雷蛇", "阿消"],
+ "dormitory_2": ["推进之王", "琴柳", "德克萨斯", "能天使", "拉普兰德"],
+ "dormitory_3": ["夜莺", "波登可", "巫恋", "柏喙", "慕斯"],
+ "dormitory_4": ["空", "Lancet-2", "艾雅法拉", "陈", "红"],
+ "meeting": ["远山", "星极"],
+ "room_1_1": ["安比尔", "空弦", "缠丸"],
+ "room_1_2": ["赫默", "杰西卡", "调香师"],
+ "room_1_3": ["伊芙利特"],
+ "room_2_1": ["夜刀", "古米", "空爆"],
+ "room_2_2": ["香草", "史都华德", "刻俄柏"],
+ "room_2_3": ["深靛"],
+ "room_3_1": ["芬", "稀音", "克洛丝"],
+ "room_3_2": ["炎熔"],
+ "room_3_3": ["清流", "森蚺", "温蒂"],
+ },
+ "plan_4": {
+ # 'contact': ['普罗旺斯'],
+ "dormitory_1": ["杜林", "闪灵", "断罪者", "食铁兽", "槐琥"],
+ "dormitory_2": ["推进之王", "琴柳", "红豆", "霜叶", "白雪"],
+ "dormitory_3": ["夜莺", "波登可", "Castle-3", "梅尔", "白面鸮"],
+ "dormitory_4": ["空", "Lancet-2", "砾", "夜烟", "斑点"],
+ # 'meeting': ['远山', '星极'],
+ # 'room_1_1': ['安比尔', '空弦', '缠丸'],
+ # 'room_1_2': ['赫默', '杰西卡', '调香师'],
+ # 'room_1_3': ['伊芙利特'],
+ # 'room_2_1': ['夜刀', '古米', '空爆'],
+ # 'room_2_2': ['香草', '史都华德', '刻俄柏'],
+ # 'room_2_3': ['深靛'],
+ # 'room_3_1': ['芬', '稀音', '克洛丝'],
+ # 'room_3_2': ['炎熔'],
+ # 'room_3_3': ['清流', '森蚺', '温蒂']
+ },
}
def debuglog():
- '''
+ """
在屏幕上输出调试信息,方便调试和报错
- '''
- logger.handlers[0].setLevel('DEBUG')
+ """
+ logger.handlers[0].setLevel("DEBUG")
def savelog():
- '''
+ """
指定日志和截屏的保存位置,方便调试和报错
调试信息和截图默认保存在代码所在的目录下
- '''
- config.LOGFILE_PATH = './log'
- config.SCREENSHOT_PATH = './screenshot'
- config.SCREENSHOT_MAXNUM = 1000
- config.ADB_DEVICE = maa_config['maa_adb']
- config.ADB_CONNECT = maa_config['maa_adb']
- config.PASSWORD = '你的密码'
+ """
+ config.LOGFILE_PATH = "./log"
+ config.SCREENSHOT_PATH = "./screenshot"
+ config.SCREENSHOT_MAXNUM = 100
init_fhlr()
-def inialize(tasks,scheduler=None):
- device = Device()
- cli = Solver(device)
- if scheduler is None:
- base_scheduler = BaseSchedulerSolver(cli.device, cli.recog)
- base_scheduler.operators = {}
- base_scheduler.global_plan = plan
- base_scheduler.current_base = {}
- base_scheduler.resting=[]
- base_scheduler.dorm_count=4
- base_scheduler.tasks = tasks
- # 读取心情开关,有菲亚梅塔或者希望全自动换班得设置为 true
- base_scheduler.read_mood = True
- base_scheduler.scan_time = {}
- base_scheduler.last_room = ''
- base_scheduler.free_blacklist = free_blacklist
- base_scheduler.resting_treshhold=resting_treshhold
- base_scheduler.MAA = None
- base_scheduler.email_config = email_config
- base_scheduler.ADB_CONNECT = config.ADB_CONNECT[0]
- base_scheduler.maa_config = maa_config
- base_scheduler.error = False
- base_scheduler.agent_base_config = agent_base_config
- return base_scheduler
- else :
- scheduler.device=cli.device
- scheduler.recog=cli.recog
- scheduler.handle_error(True)
- return scheduler
def simulate():
- '''
+ """
具体调用方法可见各个函数的参数说明
- '''
- global ope_list
- # 第一次执行任务
- # tasks = [{"plan": {'room_1_1': ['能天使','但书','龙舌兰']}, "time": datetime.now()}]
- tasks =[]
- reconnect_max_tries = 10
- reconnect_tries = 0
- base_scheduler = inialize(tasks)
-
+ """
+ global ope_lists
+ cli = Solver()
+ cli.mail() # 邮件
+ cli.base(
+ clue_collect=True, drone_room=drone_room, fia_room=fia_room, arrange=plan
+ ) # 基建
+ cli.credit() # 信用
+ ope_lists = cli.ope(eliminate=True, plan=ope_lists) # 行动,返回未完成的作战计划
+ cli.shop(shop_priority) # 商店
+ cli.recruit() # 公招
+ cli.mission() # 任务
+
+
+def schedule_task():
+ """
+ 定期运行任务
+ """
+ schedule.every().day.at("07:00").do(simulate)
+ schedule.every().day.at("19:00").do(simulate)
while True:
- try:
- if len(base_scheduler.tasks) > 0:
- (base_scheduler.tasks.sort(key=lambda x: x["time"], reverse=False))
- sleep_time = (base_scheduler.tasks[0]["time"] - datetime.now()).total_seconds()
- logger.info(base_scheduler.tasks)
- base_scheduler.send_email(base_scheduler.tasks)
- # 如果任务间隔时间超过9分钟则启动MAA
- if sleep_time > 540:
- base_scheduler.maa_plan_solver()
- elif sleep_time > 0 : time.sleep(sleep_time)
- base_scheduler.run()
- reconnect_tries = 0
- except ConnectionError as e:
- reconnect_tries +=1
- if reconnect_tries < reconnect_max_tries:
- logger.warning(f'连接端口断开....正在重连....')
- connected = False
- while not connected:
- try:
- base_scheduler = inialize([],base_scheduler)
- break
- except Exception as ce:
- logger.error(ce)
- time.sleep(5)
- continue
- continue
- else:
- raise Exception(e)
- except Exception as E:
- logger.exception(f"程序出错--->{E}")
- # cli.credit() # 信用
- # ope_lists = cli.ope(eliminate=True, plan=ope_lists) # 行动,返回未完成的作战计划
- # cli.shop(shop_priority) # 商店
- # cli.recruit() # 公招
- # cli.mission() # 任务
+ schedule.run_pending()
+ time.sleep(60)
-# debuglog()
+debuglog()
savelog()
simulate()
+schedule_task()
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100644
index 000000000..9a04e1c01
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+ln -s ui/dist .
+
+# 启动Xvfb
+Xvfb :99 -screen 0 1280x1024x24 &
+
+export DISPLAY=":99"
+
+# 等待Xvfb启动完成
+sleep 5
+
+# 启动应用
+dbus-run-session -- ./venv/bin/python /app/webview_ui.py
\ No newline at end of file
diff --git a/extract_restype.py b/extract_restype.py
new file mode 100644
index 000000000..b810353b1
--- /dev/null
+++ b/extract_restype.py
@@ -0,0 +1,38 @@
+from pathlib import Path
+
+res_path_name = "arknights_mower/resources/"
+res_path = Path(res_path_name)
+
+data = "from typing import Literal\n\nRes = Literal[\n"
+references = {}
+
+for i in res_path.glob("**/*.png"):
+ res_name = i.as_posix()
+ res_name = res_name.replace(res_path_name, "")
+ res_name = res_name.replace(".png", "")
+ data += f' "{res_name}",\n'
+ references[res_name] = []
+
+data += "]\n"
+
+with open("arknights_mower/utils/typealias/res.py", "w", encoding="utf-8") as f:
+ f.write(data)
+
+for py_file in Path("arknights_mower").glob("**/*.py"):
+ posix_path = py_file.as_posix()
+ if posix_path == "arknights_mower/utils/typealias/res.py":
+ continue
+ with py_file.open("r", encoding="utf-8") as f:
+ content = f.read()
+ for name, matches in references.items():
+ if name in content:
+ matches.append(posix_path)
+
+for name, matches in references.items():
+ if len(matches) > 0:
+ print(name)
+ for m in matches:
+ print(f" {m}")
+for name, matches in references.items():
+ if len(matches) == 0:
+ print(f"[WARN]{name}")
diff --git a/extract_scene.py b/extract_scene.py
new file mode 100644
index 000000000..bcbde6e1a
--- /dev/null
+++ b/extract_scene.py
@@ -0,0 +1,18 @@
+from arknights_mower.data import scene_list
+
+scene_class = "class Scene:"
+scene_comment = "SceneComment = {"
+
+
+for scene, data in scene_list.items():
+ id = int(scene)
+ label = data["label"]
+ comment = data["comment"]
+ scene_class += f'\n {label} = {id}\n "{comment}"'
+ scene_comment += f'\n {id}: "{comment}",'
+
+scene_comment += "\n}"
+code = scene_class + "\n\n\n" + scene_comment + "\n"
+
+with open("./arknights_mower/utils/scene.py", "w", encoding="utf-8") as f:
+ f.write(code)
diff --git a/img/log.png b/img/log.png
new file mode 100644
index 000000000..c45d27601
Binary files /dev/null and b/img/log.png differ
diff --git a/img/plan-editor.png b/img/plan-editor.png
new file mode 100644
index 000000000..d9bfff84f
Binary files /dev/null and b/img/plan-editor.png differ
diff --git a/img/riic-report.png b/img/riic-report.png
new file mode 100644
index 000000000..db272a2fe
Binary files /dev/null and b/img/riic-report.png differ
diff --git a/img/settings.png b/img/settings.png
new file mode 100644
index 000000000..5cd3d4388
Binary files /dev/null and b/img/settings.png differ
diff --git a/main.py b/main.py
deleted file mode 100644
index 0bd9c69d7..000000000
--- a/main.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import os
-import traceback
-
-from arknights_mower.__main__ import main
-from arknights_mower import __cli__
-
-if __name__ == '__main__':
- try:
- main(module=False)
- except Exception:
- print(traceback.format_exc())
- except SystemExit:
- pass
- finally:
- if not __cli__:
- os.system('pause')
diff --git a/main.spec b/main.spec
deleted file mode 100644
index da6c8b840..000000000
--- a/main.spec
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- mode: python ; coding: utf-8 -*-
-
-
-block_cipher = None
-
-
-a = Analysis(
- ['main.py'],
- pathex=[],
- binaries=[],
- datas=[
- ('arknights_mower/fonts', 'arknights_mower/__init__/fonts'),
- ('arknights_mower/models', 'arknights_mower/__init__/models'),
- ('arknights_mower/templates', 'arknights_mower/__init__/templates'),
- ('arknights_mower/resources', 'arknights_mower/__init__/resources'),
- ('arknights_mower/data', 'arknights_mower/__init__/data'),
- ('arknights_mower/vendor', 'arknights_mower/__init__/vendor'),
- ('venv64/Lib/site-packages/onnxruntime/capi/onnxruntime_providers_shared.dll', 'onnxruntime/capi/'),
- ('venv64/Lib/site-packages/shapely/DLLs/geos.dll', '.'),
- ('venv64/Lib/site-packages/shapely/DLLs/geos_c.dll', '.')
- ],
- hiddenimports=[],
- hookspath=[],
- hooksconfig={},
- runtime_hooks=[],
- excludes=[],
- win_no_prefer_redirects=False,
- win_private_assemblies=False,
- cipher=block_cipher,
- noarchive=False,
-)
-pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
-
-exe = EXE(
- pyz,
- a.scripts,
- a.binaries,
- a.zipfiles,
- a.datas,
- [],
- name='main',
- debug=False,
- bootloader_ignore_signals=False,
- strip=False,
- upx=True,
- upx_exclude=[],
- runtime_tmpdir=None,
- console=True,
- disable_windowed_traceback=False,
- argv_emulation=False,
- target_arch=None,
- codesign_identity=None,
- entitlements_file=None,
-)
diff --git a/manager.py b/manager.py
new file mode 100755
index 000000000..23bb71760
--- /dev/null
+++ b/manager.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+
+import json
+
+import webview
+
+
+class Api:
+ def __init__(self):
+ try:
+ with open("instances.json", "r", encoding="utf-8") as f:
+ self.instances = json.load(f)
+ except Exception:
+ self.instances = []
+ self.save()
+
+ def save(self):
+ with open("instances.json", "w", encoding="utf-8") as f:
+ json.dump(self.instances, f, ensure_ascii=False)
+
+ def get_instances(self):
+ return self.instances
+
+ def add(self, name, path):
+ self.instances.append({"name": name, "path": path})
+ self.save()
+
+ def remove(self, idx):
+ del self.instances[idx]
+ self.save()
+
+ def rename(self, idx, name):
+ self.instances[idx]["name"] = name
+ self.save()
+
+ def select_path(self, idx):
+ window = webview.active_window()
+ folder = window.create_file_dialog(dialog_type=webview.FOLDER_DIALOG)
+ if folder is None:
+ return None
+ if not isinstance(folder, str):
+ folder = folder[0]
+ self.instances[idx]["path"] = folder
+ self.save()
+ return folder
+
+ def start(self, idx):
+ import platform
+ import sys
+ from subprocess import Popen
+
+ is_win = platform.system() == "Windows"
+ frozen = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
+ if is_win and frozen:
+ Popen(["mower.exe", self.instances[idx]["path"]])
+ else:
+ if is_win:
+ Popen(["python.exe", "webview_ui.py", self.instances[idx]["path"]])
+ else:
+ Popen(["python3", "webview_ui.py", self.instances[idx]["path"]])
+
+
+def jump_to_index(window):
+ window.load_url("/manager/index.html")
+
+
+if __name__ == "__main__":
+ api = Api()
+ window = webview.create_window(
+ title="多开管理器",
+ url="dist/index.html",
+ js_api=api,
+ min_size=(400, 500),
+ width=400,
+ height=500,
+ )
+ webview.start(jump_to_index, window, http_server=True)
diff --git a/menu.py b/menu.py
deleted file mode 100644
index 7957b8ae4..000000000
--- a/menu.py
+++ /dev/null
@@ -1,363 +0,0 @@
-import json
-from multiprocessing import Pipe, Process, freeze_support
-import time
-from datetime import datetime
-import PySimpleGUI as sg
-import os
-from ruamel.yaml import YAML
-from arknights_mower.solvers.base_schedule import BaseSchedulerSolver
-from arknights_mower.strategy import Solver
-from arknights_mower.utils.device import Device
-from arknights_mower.utils.log import logger, init_fhlr
-from arknights_mower.utils import config
-from arknights_mower.data import agent_list
-
-yaml = YAML()
-confUrl = './conf.yml';
-conf = {}
-plan = {}
-global window
-buffer = ''
-line = 0
-half_line_index = 0
-
-
-def inialize(tasks, scheduler=None):
- device = Device()
- cli = Solver(device)
- if scheduler is None:
- base_scheduler = BaseSchedulerSolver(cli.device, cli.recog)
- base_scheduler.operators = {}
- plan1 = {}
- for key in plan:
- plan1[key] = plan[key]['plans']
- base_scheduler.global_plan = {'default': "plan_1", "plan_1": plan1}
- base_scheduler.current_base = {}
- base_scheduler.resting = []
- base_scheduler.dorm_count = 4
- base_scheduler.tasks = tasks
- # 读取心情开关,有菲亚梅塔或者希望全自动换班得设置为 true
- base_scheduler.read_mood = True
- base_scheduler.scan_time = {}
- base_scheduler.last_room = ''
- base_scheduler.free_blacklist = []
- base_scheduler.resting_treshhold = 0.5
- base_scheduler.MAA = None
- base_scheduler.ADB_CONNECT = config.ADB_CONNECT[0]
- base_scheduler.error = False
- return base_scheduler
- else:
- scheduler.device = cli.device
- scheduler.recog = cli.recog
- scheduler.handle_error(True)
- return scheduler
-
-
-def simulate():
- '''
- 具体调用方法可见各个函数的参数说明
- '''
- tasks = []
- reconnect_max_tries = 10
- reconnect_tries = 0
- base_scheduler = inialize(tasks)
- while True:
- try:
- if len(base_scheduler.tasks) > 0:
- (base_scheduler.tasks.sort(key=lambda x: x["time"], reverse=False))
- sleep_time = (base_scheduler.tasks[0]["time"] - datetime.now()).total_seconds()
- logger.info(base_scheduler.tasks)
- if sleep_time >= 0:
- remaining_time = (base_scheduler.tasks[0]["time"] - datetime.now()).total_seconds()
- logger.info(f"开始休息 {'%.2f' % (remaining_time/60)} 分钟,到{base_scheduler.tasks[0]['time'].strftime('%H:%M:%S')}")
- time.sleep(sleep_time)
- base_scheduler.run()
- reconnect_tries = 0
- except ConnectionError as e:
- reconnect_tries += 1
- if reconnect_tries < reconnect_max_tries:
- logger.warning(f'连接端口断开....正在重连....')
- connected = False
- while not connected:
- try:
- base_scheduler = inialize([], base_scheduler)
- break
- except Exception as ce:
- logger.error(ce)
- time.sleep(5)
- continue
- continue
- else:
- raise Exception(e)
- except Exception as E:
- logger.exception(f"程序出错--->{E}")
-
-
-# 读取写入配置文件
-def loadConf():
- global conf
- global confUrl
- if not os.path.isfile(confUrl):
- open(confUrl, 'w') # 创建空配置文件
- conf['planFile'] = './plan.json' # 默认排班表地址
- return
- with open(confUrl, 'r', encoding='utf8') as c:
- conf = yaml.load(c)
- if conf is None:
- conf = {}
-
-
-def writeConf():
- global conf
- global confUrl
- with open(confUrl, 'w', encoding='utf8') as c:
- yaml.default_flow_style = False
- yaml.dump(conf, c)
-
-
-# 读取写入排班表
-def loadPlan(url):
- global plan
- if not os.path.isfile(url):
- with open(url, 'w') as f:
- json.dump(plan, f) # 创建空json文件
- return
- try:
- with open(url, 'r', encoding='utf8') as fp:
- plan = json.loads(fp.read())
- conf['planFile'] = url
- for i in range(1, 4):
- for j in range(1, 4):
- window[f'btn_room_{str(i)}_{str(j)}'].update('待建造', button_color=('white', '#4f4945'))
- for key in plan:
- if type(plan[key]).__name__ == 'list': # 兼容旧版格式
- plan[key] = {'plans': plan[key], 'name': ''}
- elif plan[key]['name'] == '贸易站':
- window['btn_' + key].update('贸易站', button_color=('#4f4945', '#33ccff'))
- elif plan[key]['name'] == '制造站':
- window['btn_' + key].update('制造站', button_color=('#4f4945', '#ffcc00'))
- elif plan[key]['name'] == '发电站':
- window['btn_' + key].update('发电站', button_color=('#4f4945', '#ccff66'))
- except Exception as e:
- logger.error(e)
- println('json格式错误!')
-
-
-def writePlan():
- with open(conf['planFile'], 'w', encoding='utf8') as c:
- json.dump(plan, c, ensure_ascii=False)
-
-
-# 执行自动排班
-def start(c, p, child_conn):
- global plan
- global conf
- conf = c
- plan = p
- config.LOGFILE_PATH = './log'
- config.SCREENSHOT_PATH = './screenshot'
- config.SCREENSHOT_MAXNUM = 1000
- config.ADB_DEVICE = [conf['adb']]
- config.ADB_CONNECT = [conf['adb']]
- init_fhlr(child_conn)
- logger.info('开始运行Mower')
- simulate()
-
-
-# 主页面
-def menu():
- global window
- global buffer
- loadConf()
- sg.theme('LightBlue2')
- # maa_title = sg.Text('MAA:')
- # maa_select = sg.InputCombo(['启用', '停用'], default_value='停用', size=(20, 3))
- # adb
- adb_title = sg.Text('adb连接地址:', size=10)
- adb = sg.InputText(conf['adb'] if 'adb' in conf.keys() else '', size=60)
- # 排班表json
- plan_title = sg.Text('排班表:', size=10)
- planFile = sg.InputText(conf['planFile'], readonly=True, size=60, key='planFile', enable_events=True)
- plan_select = sg.FileBrowse('...', size=(3, 1), file_types=(("JSON files", "*.json"),))
- # 总开关
- on_btn = sg.Button('开始执行', key='on')
- off_btn = sg.Button('立即停止', key='off', visible=False, button_color='red')
- # 日志栏
- output = sg.Output(size=(150, 25), key='log', text_color='#808069', font=('微软雅黑', 9))
-
- # 宿舍区
- central = sg.Button('控制中枢', key='btn_central', size=(18, 3), button_color='#303030')
- dormitory_1 = sg.Button('宿舍', key='btn_dormitory_1', size=(18, 2), button_color='#303030')
- dormitory_2 = sg.Button('宿舍', key='btn_dormitory_2', size=(18, 2), button_color='#303030')
- dormitory_3 = sg.Button('宿舍', key='btn_dormitory_3', size=(18, 2), button_color='#303030')
- dormitory_4 = sg.Button('宿舍', key='btn_dormitory_4', size=(18, 2), button_color='#303030')
- centralArea = sg.Column([[central], [dormitory_1], [dormitory_2], [dormitory_3], [dormitory_4]])
- # 制造站区
- room_1_1 = sg.Button('待建造', key='btn_room_1_1', size=(12, 2), button_color='#4f4945')
- room_1_2 = sg.Button('待建造', key='btn_room_1_2', size=(12, 2), button_color='#4f4945')
- room_1_3 = sg.Button('待建造', key='btn_room_1_3', size=(12, 2), button_color='#4f4945')
- room_2_1 = sg.Button('待建造', key='btn_room_2_1', size=(12, 2), button_color='#4f4945')
- room_2_2 = sg.Button('待建造', key='btn_room_2_2', size=(12, 2), button_color='#4f4945')
- room_2_3 = sg.Button('待建造', key='btn_room_2_3', size=(12, 2), button_color='#4f4945')
- room_3_1 = sg.Button('待建造', key='btn_room_3_1', size=(12, 2), button_color='#4f4945')
- room_3_2 = sg.Button('待建造', key='btn_room_3_2', size=(12, 2), button_color='#4f4945')
- room_3_3 = sg.Button('待建造', key='btn_room_3_3', size=(12, 2), button_color='#4f4945')
- leftArea = sg.Column([[room_1_1, room_1_2, room_1_3],
- [room_2_1, room_2_2, room_2_3],
- [room_3_1, room_3_2, room_3_3]])
- # 功能区
- meeting = sg.Button('会客室', key='btn_meeting', size=(24, 2), button_color='#303030')
- factory = sg.Button('加工站', key='btn_factory', size=(24, 2), button_color='#303030')
- contact = sg.Button('办公室', key='btn_contact', size=(24, 2), button_color='#303030')
- rightArea = sg.Column([[meeting], [factory], [contact]])
-
- settingLayout = [[sg.Column([[sg.Text('设施类别:'), sg.InputCombo(['贸易站', '制造站', '发电站'], size=12, key='station_type')]],
- key='station_type_col', visible=False)]]
- # 排班表设置标签
- for i in range(1, 6):
- setArea = sg.Column([[sg.Text('干员:'),
- sg.InputCombo(['Free'] + agent_list, size=20, key='agent' + str(i)),
- sg.Text('组:'),
- sg.InputText('', size=15, key='group' + str(i)),
- sg.Text('替换:'),
- sg.InputText('', size=30, key='replacement' + str(i))
- ]], key='setArea' + str(i), visible=False)
- settingLayout.append([setArea])
- settingLayout.append([sg.Button('保存', key='savePlan', visible=False)])
- settingArea = sg.Column(settingLayout, element_justification="center",
- vertical_alignment="bottom",
- expand_x=True)
- # 组装页面
- mainTab = sg.Tab(' 主页 ', [[adb_title, adb],
- [plan_title, planFile, plan_select],
- [output],
- [on_btn, off_btn]])
-
- planTab = sg.Tab(' 排班表 ', [[leftArea, centralArea, rightArea], [settingArea]], element_justification="center")
- window = sg.Window('Mower', [[sg.TabGroup([[mainTab, planTab]], border_width=0,
- tab_border_width=0, focus_color='#bcc8e5',
- selected_background_color='#d4dae8', background_color='#aab6d3',
- tab_background_color='#aab6d3')]], font='微软雅黑', finalize=True)
-
- loadPlan(conf['planFile'])
- while True:
- event, value = window.Read()
- if event == sg.WIN_CLOSED:
- conf['adb'] = adb.get()
- break
- elif event == 'planFile' and planFile.get() != conf['planFile']:
- writePlan()
- loadPlan(planFile.get())
- planFile.update(conf['planFile'])
- elif event.startswith('btn_'):
- btn = event
- initBtn(event)
- elif event == 'savePlan':
- saveBtn(btn)
- elif event == 'on':
- if adb.get() == '':
- println('adb未设置!')
- continue
- conf['adb'] = adb.get()
-
- on_btn.update(visible=False)
- off_btn.update(visible=True)
- clear()
- parent_conn, child_conn = Pipe()
- main_thread = Process(target=start, args=(conf, plan, child_conn), daemon=True)
- main_thread.start()
- window.perform_long_operation(lambda: log(parent_conn), 'log')
- elif event == 'off':
- println('停止运行')
- child_conn.close()
- main_thread.terminate()
- on_btn.update(visible=True)
- off_btn.update(visible=False)
-
- window.close()
- writeConf()
- writePlan()
-
-
-def initBtn(event):
- room_key = event[4:]
- station_name = plan[room_key]['name'] if room_key in plan.keys() else ''
- plans = plan[room_key]['plans'] if room_key in plan.keys() else []
- if room_key.startswith('room'):
- window['station_type_col'].update(visible=True)
- window['station_type'].update(station_name)
- visibleCnt = 3 # 设施干员需求数量
- else:
- if room_key == 'meeting':
- visibleCnt = 2
- elif room_key == 'factory' or room_key == 'contact':
- visibleCnt = 1
- else:
- visibleCnt = 5
- window['station_type_col'].update(visible=False)
- window['station_type'].update('')
- window['savePlan'].update(visible=True)
- for i in range(1, 6):
- if i > visibleCnt:
- window['setArea' + str(i)].update(visible=False)
- window['agent' + str(i)].update('')
- window['group' + str(i)].update('')
- window['replacement' + str(i)].update('')
- else:
- window['setArea' + str(i)].update(visible=True)
- window['agent' + str(i)].update(plans[i - 1]['agent'] if len(plans) >= i else '')
- window['group' + str(i)].update(plans[i - 1]['group'] if len(plans) >= i else '')
- window['replacement' + str(i)].update(','.join(plans[i - 1]['replacement']) if len(plans) >= i else '')
-
-
-def saveBtn(btn):
- plan1 = {'name': window['station_type'].get(), 'plans': []}
- for i in range(1, 6):
- agent = window['agent' + str(i)].get()
- group = window['group' + str(i)].get()
- replacement = list(filter(None, window['replacement' + str(i)].get().split(',')))
- if agent != '':
- plan1['plans'].append({'agent': agent, 'group': group, 'replacement': replacement})
- plan[btn[4:]] = plan1
- writePlan()
- loadPlan(conf['planFile'])
-
-
-# 输出日志
-def log(pipe):
- try:
- while True:
- msg = pipe.recv()
- println(msg)
- except EOFError:
- pipe.close()
-
-
-def println(msg):
- global buffer
- global line
- global half_line_index
- maxLen = 500 # 最大行数
- buffer = f'{buffer}\n{time.strftime("%m-%d %H:%M:%S")} {msg}'.strip()
- window['log'].update(value=buffer)
- if line == maxLen // 2:
- half_line_index = len(buffer)
- if line >= maxLen:
- buffer = buffer[half_line_index:]
- line = maxLen // 2
- else:
- line += 1
-
-
-# 清空输出栏
-def clear():
- global buffer
- global line
- buffer = ''
- window['log'].update(value=buffer)
- line = 0
-
-
-if __name__ == '__main__':
- freeze_support()
- menu()
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 000000000..b1fe93c81
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1,58 @@
+# 图像处理
+opencv-python==4.9.0.80
+numpy==1.26.4
+scipy==1.13.0
+scikit-image==0.23.2
+scikit-learn==1.4.2
+pillow==10.3.0
+
+# OCR
+rapidocr-onnxruntime==1.3.24
+
+# 二维码
+base45==0.4.4
+pyzbar==0.1.9
+qrcode==7.4.2
+
+# WebUI
+Flask==3.0.3
+Flask-Cors==4.0.1
+flask-sock==0.7.0
+pywebview==5.1
+pystray==0.19.5
+
+# 配置文件
+PyYAML==6.0.1
+yamlcore==0.0.2
+pydantic==2.8.2
+
+# CSV
+pandas==2.2.2
+
+# 日志
+colorlog==6.8.2
+
+# 场景图
+networkx==3.3
+
+# 副表条件
+evalidate==2.0.2
+
+# 任务列表
+jsonpickle==3.0.4
+
+# 时区
+tzlocal==5.2
+pytz==2024.1
+
+# 网络请求、官网公告解析、HTML转Markdown、检查版本
+requests==2.31.0
+beautifulsoup4==4.12.3
+lxml==5.2.2
+htmllistparse==0.6.1
+
+# 邮件模板
+Jinja2==3.1.4
+
+# 启动模拟器后按快捷键
+PyAutoGUI==0.9.54
diff --git a/requirements.txt b/requirements.txt
index 1957f00ae..d21400d17 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,20 +1,218 @@
-opencv_python==4.5.5.64
-colorlog==6.4.1
-matplotlib==3.5.2
-numpy==1.21.2
-scikit_image==0.18.3
-scikit_learn==1.0
-onnxruntime==1.9.0
-pyclipper==1.3.0
-shapely==1.7.1
-tornado==6.1
-requests==2.22.0
-schedule~=1.1.0
-setuptools~=60.2.0
-Pillow~=9.2.0
-pytesseract~=0.3.10
-pandas==1.5.2
-pyyaml==6.0
-PySimpleGUI~=4.60.4
-pytz~=2022.6
-paddleocr==2.6.1.3
+#
+# This file is autogenerated by pip-compile with Python 3.12
+# by the following command:
+#
+# pip-compile requirements.in
+#
+annotated-types==0.7.0
+ # via pydantic
+base45==0.4.4
+ # via -r requirements.in
+beautifulsoup4==4.12.3
+ # via
+ # -r requirements.in
+ # htmllistparse
+blinker==1.8.2
+ # via flask
+bottle==0.12.25
+ # via pywebview
+certifi==2024.7.4
+ # via requests
+charset-normalizer==3.3.2
+ # via requests
+click==8.1.7
+ # via flask
+coloredlogs==15.0.1
+ # via onnxruntime
+colorlog==6.8.2
+ # via -r requirements.in
+evalidate==2.0.2
+ # via -r requirements.in
+flask==3.0.3
+ # via
+ # -r requirements.in
+ # flask-cors
+ # flask-sock
+flask-cors==4.0.1
+ # via -r requirements.in
+flask-sock==0.7.0
+ # via -r requirements.in
+flatbuffers==24.3.25
+ # via onnxruntime
+fusepy==3.0.1
+ # via htmllistparse
+h11==0.14.0
+ # via wsproto
+html5lib==1.1
+ # via htmllistparse
+htmllistparse==0.6.1
+ # via -r requirements.in
+humanfriendly==10.0
+ # via coloredlogs
+idna==3.7
+ # via requests
+imageio==2.34.2
+ # via scikit-image
+itsdangerous==2.2.0
+ # via flask
+jinja2==3.1.4
+ # via
+ # -r requirements.in
+ # flask
+joblib==1.4.2
+ # via scikit-learn
+jsonpickle==3.0.4
+ # via -r requirements.in
+lazy-loader==0.4
+ # via scikit-image
+lxml==5.2.2
+ # via -r requirements.in
+markupsafe==2.1.5
+ # via
+ # jinja2
+ # werkzeug
+mouseinfo==0.1.3
+ # via pyautogui
+mpmath==1.3.0
+ # via sympy
+networkx==3.3
+ # via
+ # -r requirements.in
+ # scikit-image
+numpy==1.26.4
+ # via
+ # -r requirements.in
+ # imageio
+ # onnxruntime
+ # opencv-python
+ # pandas
+ # rapidocr-onnxruntime
+ # scikit-image
+ # scikit-learn
+ # scipy
+ # shapely
+ # tifffile
+onnxruntime==1.18.1
+ # via rapidocr-onnxruntime
+opencv-python==4.9.0.80
+ # via
+ # -r requirements.in
+ # rapidocr-onnxruntime
+packaging==24.1
+ # via
+ # lazy-loader
+ # onnxruntime
+ # scikit-image
+pandas==2.2.2
+ # via -r requirements.in
+pillow==10.3.0
+ # via
+ # -r requirements.in
+ # imageio
+ # pystray
+ # rapidocr-onnxruntime
+ # scikit-image
+protobuf==5.27.2
+ # via onnxruntime
+proxy-tools==0.1.0
+ # via pywebview
+pyautogui==0.9.54
+ # via -r requirements.in
+pyclipper==1.3.0.post5
+ # via rapidocr-onnxruntime
+pydantic==2.8.2
+ # via -r requirements.in
+pydantic-core==2.20.1
+ # via pydantic
+pygetwindow==0.0.9
+ # via pyautogui
+pymsgbox==1.0.9
+ # via pyautogui
+pyperclip==1.9.0
+ # via mouseinfo
+pypng==0.20220715.0
+ # via qrcode
+pyrect==0.2.0
+ # via pygetwindow
+pyscreeze==0.1.30
+ # via pyautogui
+pystray==0.19.5
+ # via -r requirements.in
+python-dateutil==2.9.0.post0
+ # via pandas
+python-xlib==0.33
+ # via pystray
+python3-xlib==0.15
+ # via
+ # mouseinfo
+ # pyautogui
+pytweening==1.2.0
+ # via pyautogui
+pytz==2024.1
+ # via
+ # -r requirements.in
+ # pandas
+pywebview==5.1
+ # via -r requirements.in
+pyyaml==6.0.1
+ # via
+ # -r requirements.in
+ # rapidocr-onnxruntime
+pyzbar==0.1.9
+ # via -r requirements.in
+qrcode==7.4.2
+ # via -r requirements.in
+rapidocr-onnxruntime==1.3.24
+ # via -r requirements.in
+requests==2.31.0
+ # via
+ # -r requirements.in
+ # htmllistparse
+scikit-image==0.23.2
+ # via -r requirements.in
+scikit-learn==1.4.2
+ # via -r requirements.in
+scipy==1.13.0
+ # via
+ # -r requirements.in
+ # scikit-image
+ # scikit-learn
+shapely==2.0.5
+ # via rapidocr-onnxruntime
+simple-websocket==1.0.0
+ # via flask-sock
+six==1.16.0
+ # via
+ # html5lib
+ # pystray
+ # python-dateutil
+ # python-xlib
+ # rapidocr-onnxruntime
+soupsieve==2.5
+ # via beautifulsoup4
+sympy==1.13.0
+ # via onnxruntime
+threadpoolctl==3.5.0
+ # via scikit-learn
+tifffile==2024.7.2
+ # via scikit-image
+typing-extensions==4.12.2
+ # via
+ # pydantic
+ # pydantic-core
+ # pywebview
+ # qrcode
+tzdata==2024.1
+ # via pandas
+tzlocal==5.2
+ # via -r requirements.in
+urllib3==2.2.1
+ # via requests
+webencodings==0.5.1
+ # via html5lib
+werkzeug==3.0.3
+ # via flask
+wsproto==1.2.0
+ # via simple-websocket
+yamlcore==0.0.2
+ # via -r requirements.in
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 000000000..895c7e570
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,78 @@
+# Exclude a variety of commonly ignored directories.
+exclude = [
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".ipynb_checkpoints",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pyenv",
+ ".pytest_cache",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ ".vscode",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "site-packages",
+ "venv",
+ "ArknightsGameResource",
+]
+
+# Same as Black.
+line-length = 88
+indent-width = 4
+
+# Assume Python 3.12
+target-version = "py312"
+
+[lint]
+# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
+# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
+# McCabe complexity (`C901`) by default.
+select = ["E4", "E7", "E9", "F", "I"]
+ignore = []
+
+# Allow fix for all enabled rules (when `--fix`) is provided.
+fixable = ["ALL"]
+unfixable = []
+
+# Allow unused variables when underscore-prefixed.
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+
+[format]
+# Like Black, use double quotes for strings.
+quote-style = "double"
+
+# Like Black, indent with spaces, rather than tabs.
+indent-style = "space"
+
+# Like Black, respect magic trailing commas.
+skip-magic-trailing-comma = false
+
+# Like Black, automatically detect the appropriate line ending.
+line-ending = "auto"
+
+# Enable auto-formatting of code examples in docstrings. Markdown,
+# reStructuredText code/literal blocks and doctests are all supported.
+#
+# This is currently disabled by default, but it is planned for this
+# to be opt-out in the future.
+docstring-code-format = false
+
+# Set the line length limit used when formatting code snippets in
+# docstrings.
+#
+# This only has an effect when the `docstring-code-format` setting is
+# enabled.
+docstring-code-line-length = "dynamic"
diff --git a/server.py b/server.py
new file mode 100755
index 000000000..d0f07e808
--- /dev/null
+++ b/server.py
@@ -0,0 +1,630 @@
+#!/usr/bin/env python3
+import copy
+import datetime
+import json
+import mimetypes
+import os
+import pathlib
+import sys
+import time
+from functools import wraps
+from io import BytesIO
+from threading import Thread
+
+import pytz
+from flask import Flask, abort, request, send_file, send_from_directory
+from flask_cors import CORS
+from flask_sock import Sock
+from tzlocal import get_localzone
+from werkzeug.exceptions import NotFound
+
+from arknights_mower import __system__
+from arknights_mower.utils import config
+from arknights_mower.utils.log import logger
+from arknights_mower.utils.path import get_path
+
+mimetypes.add_type("text/html", ".html")
+mimetypes.add_type("text/css", ".css")
+mimetypes.add_type("application/javascript", ".js")
+
+app = Flask(__name__, static_folder="dist", static_url_path="")
+sock = Sock(app)
+CORS(app)
+
+mower_thread = None
+log_lines = []
+ws_connections = []
+saved_state = None
+
+
+def read_log():
+ global log_lines
+ global ws_connections
+
+ while True:
+ msg = config.log_queue.get()
+ log_lines.append(msg)
+ log_lines = log_lines[-100:]
+ for ws in ws_connections:
+ ws.send(msg)
+
+
+Thread(target=read_log, daemon=True).start()
+
+
+def require_token(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if hasattr(app, "token") and request.headers.get("token", "") != app.token:
+ abort(403)
+ return f(*args, **kwargs)
+
+ return decorated_function
+
+
+@app.route("/")
+def serve_index(path):
+ return send_from_directory("dist", path)
+
+
+@app.errorhandler(404)
+def not_found(e):
+ if (path := request.path).startswith("/docs"):
+ try:
+ return send_from_directory("dist" + path, "index.html")
+ except NotFound:
+ return "404 Not Found
", 404
+ return send_from_directory("dist", "index.html")
+
+
+@app.route("/conf", methods=["GET", "POST"])
+@require_token
+def load_config():
+ if request.method == "GET":
+ return config.conf.model_dump()
+ else:
+ config.conf = config.Conf(**request.json)
+ config.save_conf()
+ return "New config saved!"
+
+
+@app.route("/plan", methods=["GET", "POST"])
+@require_token
+def load_plan_from_json():
+ if request.method == "GET":
+ return config.plan.model_dump(exclude_none=True)
+ else:
+ config.plan = config.PlanModel(**request.json)
+ config.save_plan()
+ return "New plan saved。"
+
+
+@app.route("/operator")
+def operator_list():
+ from arknights_mower.data import agent_list
+
+ return agent_list
+
+
+@app.route("/shop")
+def shop_list():
+ from arknights_mower.data import shop_items
+
+ return list(shop_items.keys())
+
+
+@app.route("/depot/readdepot")
+def read_depot():
+ from arknights_mower.utils import depot
+
+ return depot.读取仓库()
+
+
+@app.route("/running")
+def running():
+ return "true" if mower_thread and mower_thread.is_alive() else "false"
+
+
+@app.route("/start/")
+@require_token
+def start(start_type):
+ global mower_thread
+ global log_lines
+ global saved_state
+
+ if mower_thread and mower_thread.is_alive():
+ return "false"
+
+ # 创建 tmp 文件夹
+ tmp_dir = get_path("@app/tmp")
+ tmp_dir.mkdir(exist_ok=True)
+
+ config.stop_mower.clear()
+
+ from arknights_mower.__main__ import main
+
+ if saved_state is None or start_type == "2":
+ saved_state = {}
+ if start_type == "1":
+ saved_state["tasks"] = []
+ mower_thread = Thread(target=main, args=(saved_state,), daemon=True)
+ mower_thread.start()
+
+ log_lines = []
+
+ return "true"
+
+
+@app.route("/stop")
+@require_token
+def stop():
+ global mower_thread
+ global saved_state
+
+ logger.info(mower_thread)
+ if mower_thread is None:
+ return "true"
+ from arknights_mower.__main__ import base_scheduler
+
+ saved_state = {
+ "dorm": copy.deepcopy(base_scheduler.op_data.dorm),
+ "tasks": copy.deepcopy(base_scheduler.tasks),
+ "party_time": copy.deepcopy(base_scheduler.op_data.party_time),
+ "operators": copy.deepcopy(base_scheduler.op_data.operators),
+ }
+ logger.info("Mower数据已经保存")
+
+ config.stop_mower.set()
+
+ mower_thread.join(10)
+ if mower_thread.is_alive():
+ logger.error("Mower线程仍在运行")
+ return "false"
+ else:
+ logger.info("成功停止mower线程")
+ mower_thread = None
+ return "true"
+
+
+@app.route("/stop-maa")
+@require_token
+def stop_maa():
+ global mower_thread
+
+ if mower_thread is None:
+ return "true"
+
+ config.stop_maa.set()
+ return "OK"
+
+
+@sock.route("/log")
+def log(ws):
+ global ws_connections
+ global log_lines
+
+ ws.send("\n".join(log_lines))
+ ws_connections.append(ws)
+
+ from simple_websocket import ConnectionClosed
+
+ try:
+ while True:
+ ws.receive()
+ except ConnectionClosed:
+ ws_connections.remove(ws)
+
+
+def conn_send(text):
+ from arknights_mower.utils import config
+
+ if not config.webview_process.is_alive():
+ return ""
+
+ config.parent_conn.send(text)
+ return config.parent_conn.recv()
+
+
+@app.route("/dialog/file")
+@require_token
+def open_file_dialog():
+ return conn_send("file")
+
+
+@app.route("/dialog/folder")
+@require_token
+def open_folder_dialog():
+ return conn_send("folder")
+
+
+@app.route("/import", methods=["POST"])
+@require_token
+def import_from_image():
+ img = request.files["img"]
+ if img.mimetype == "application/json":
+ data = json.load(img)
+ else:
+ try:
+ from PIL import Image
+
+ from arknights_mower.utils import qrcode
+
+ img = Image.open(img)
+ data = qrcode.decode(img)
+ except Exception as e:
+ msg = f"排班表导入失败:{e}"
+ logger.exception(msg)
+ return msg
+ if data:
+ config.plan = config.PlanModel(**data)
+ config.save_plan()
+ return "排班已加载"
+ else:
+ return "排班表导入失败!"
+
+
+@app.route("/sss-copilot", methods=["GET", "POST"])
+@require_token
+def upload_sss_copilot():
+ copilot = get_path("@app/sss.json")
+ if request.method == "GET":
+ if copilot.is_file():
+ with copilot.open("r", encoding="utf-8") as f:
+ data = json.load(f)
+ else:
+ return {"exists": False}
+ else:
+ print(request.files)
+ data = request.files["copilot"]
+ data.save(copilot)
+ data.seek(0)
+ data = json.load(data)
+ return {
+ "exists": True,
+ "title": data["doc"]["title"],
+ "details": data["doc"]["details"],
+ "operators": data["opers"],
+ }
+
+
+@app.route("/dialog/save/img", methods=["POST"])
+@require_token
+def save_file_dialog():
+ img = request.files["img"]
+
+ from PIL import Image
+
+ from arknights_mower.utils import qrcode
+
+ upper = Image.open(img)
+
+ img = qrcode.export(
+ config.plan.model_dump(exclude_none=True), upper, config.conf.theme
+ )
+ buffer = BytesIO()
+ img.save(buffer, format="JPEG")
+ buffer.seek(0)
+ return send_file(buffer, "image/jpeg")
+
+
+@app.route("/export-json")
+@require_token
+def export_json():
+ return send_file(config.plan_path)
+
+
+@app.route("/check-maa")
+@require_token
+def get_maa_adb_version():
+ try:
+ asst_path = os.path.dirname(
+ pathlib.Path(config.conf.maa_path) / "Python" / "asst"
+ )
+ if asst_path not in sys.path:
+ sys.path.append(asst_path)
+ from asst.asst import Asst
+
+ Asst.load(config.conf.maa_path)
+ asst = Asst()
+ version = asst.get_version()
+ asst.set_instance_option(2, config.conf.maa_touch_option)
+ if asst.connect(config.conf.maa_adb_path, config.conf.adb):
+ maa_msg = f"Maa {version} 加载成功"
+ else:
+ maa_msg = "连接失败,请检查Maa日志!"
+ except Exception as e:
+ maa_msg = "Maa加载失败:" + str(e)
+ logger.exception(maa_msg)
+ return maa_msg
+
+
+@app.route("/maa-conn-preset")
+@require_token
+def get_maa_conn_presets():
+ try:
+ with open(
+ os.path.join(config.conf.maa_path, "resource", "config.json"),
+ "r",
+ encoding="utf-8",
+ ) as f:
+ presets = [i["configName"] for i in json.load(f)["connection"]]
+ except Exception as e:
+ logger.exception(e)
+ presets = []
+ return presets
+
+
+@app.route("/record/getMoodRatios")
+def get_mood_ratios():
+ from arknights_mower.solvers import record
+
+ return record.get_mood_ratios()
+
+
+@app.route("/getwatermark")
+def getwatermark():
+ from arknights_mower.__init__ import __version__
+
+ return __version__
+
+
+def str2date(target: str):
+ try:
+ return datetime.datetime.strptime(target, "%Y-%m-%d").date()
+ except ValueError:
+ return datetime.datetime.strptime(target, "%Y/%m/%d").date()
+
+
+def date2str(target: datetime.date):
+ try:
+ return datetime.datetime.strftime(target, "%Y-%m-%d")
+ except ValueError:
+ return datetime.datetime.strftime(target, "%Y/%m/%d")
+
+
+@app.route("/report/getReportData")
+def get_report_data():
+ import pandas as pd
+
+ record_path = get_path("@app/tmp/report.csv")
+ try:
+ format_data = []
+ if os.path.exists(record_path) is False:
+ logger.debug("基报不存在")
+ return False
+ df = pd.read_csv(record_path, encoding="gbk")
+ data = df.to_dict("records")
+ earliest_date = str2date(data[0]["Unnamed: 0"])
+
+ for item in data:
+ format_data.append(
+ {
+ "日期": date2str(
+ str2date(item["Unnamed: 0"]) - datetime.timedelta(days=1)
+ ),
+ "作战录像": item["作战录像"],
+ "赤金": item["赤金"],
+ "制造总数": int(item["赤金"] + item["作战录像"]),
+ "龙门币订单": item["龙门币订单"],
+ "反向作战录像": -item["作战录像"],
+ "龙门币订单数": item["龙门币订单数"],
+ "每单获取龙门币": int(item["龙门币订单"] / item["龙门币订单数"]),
+ }
+ )
+
+ if len(format_data) < 15:
+ for i in range(1, 16 - len(format_data)):
+ format_data.insert(
+ 0,
+ {
+ "日期": date2str(
+ earliest_date - datetime.timedelta(days=i + 1)
+ ),
+ "作战录像": "-",
+ "赤金": "-",
+ "龙门币订单": "-",
+ "龙门币订单数": "-",
+ "每单获取龙门币": "-",
+ },
+ )
+ logger.debug(format_data)
+ return format_data
+ except PermissionError:
+ logger.info("report.csv正在被占用")
+
+
+@app.route("/report/getOrundumData")
+def get_orundum_data():
+ import pandas as pd
+
+ record_path = get_path("@app/tmp/report.csv")
+ try:
+ format_data = []
+ if os.path.exists(record_path) is False:
+ logger.debug("基报不存在")
+ return False
+ df = pd.read_csv(record_path, encoding="gbk")
+ data = df.to_dict("records")
+ earliest_date = datetime.datetime.now()
+
+ begin_make_orundum = (earliest_date + datetime.timedelta(days=1)).date()
+ print(begin_make_orundum)
+ if len(data) >= 15:
+ for i in range(len(data) - 1, -1, -1):
+ if 0 < i < len(data) - 15:
+ data.pop(i)
+ else:
+ logger.debug("合成玉{}".format(data[i]["合成玉"]))
+ if data[i]["合成玉"] > 0:
+ begin_make_orundum = str2date(data[i]["Unnamed: 0"])
+ else:
+ for item in data:
+ if item["合成玉"] > 0:
+ begin_make_orundum = str2date(item["Unnamed: 0"])
+ if begin_make_orundum > earliest_date.date():
+ return format_data
+ total_orundum = 0
+ for item in data:
+ total_orundum = total_orundum + item["合成玉"]
+ format_data.append(
+ {
+ "日期": date2str(
+ str2date(item["Unnamed: 0"]) - datetime.timedelta(days=1)
+ ),
+ "合成玉": item["合成玉"],
+ "合成玉订单数量": item["合成玉订单数量"],
+ "抽数": round((item["合成玉"] / 600), 1),
+ "累计制造合成玉": total_orundum,
+ }
+ )
+
+ if len(format_data) < 15:
+ earliest_date = str2date(data[0]["Unnamed: 0"])
+ for i in range(1, 16 - len(format_data)):
+ format_data.insert(
+ 0,
+ {
+ "日期": date2str(
+ earliest_date - datetime.timedelta(days=i + 1)
+ ),
+ "合成玉": "-",
+ "合成玉订单数量": "-",
+ "抽数": "-",
+ "累计制造合成玉": 0,
+ },
+ )
+ logger.debug(format_data)
+ return format_data
+ except PermissionError:
+ logger.info("report.csv正在被占用")
+
+
+@app.route("/test-email")
+@require_token
+def test_email():
+ from arknights_mower.utils.email import Email
+
+ email = Email("mower测试邮件", config.conf.mail_subject + "测试邮件", None)
+ try:
+ email.send()
+ except Exception as e:
+ msg = "邮件发送失败!\n" + str(e)
+ logger.exception(msg)
+ return msg
+ return "邮件发送成功!"
+
+
+@app.route("/test-custom-screenshot")
+@require_token
+def test_custom_screenshot():
+ import base64
+ import subprocess
+
+ import cv2
+ import numpy as np
+
+ command = config.conf.custom_screenshot.command
+
+ start = time.time()
+ data = subprocess.check_output(
+ command,
+ shell=True,
+ creationflags=subprocess.CREATE_NO_WINDOW if __system__ == "windows" else 0,
+ )
+ end = time.time()
+ elapsed = int((end - start) * 1000)
+
+ data = np.frombuffer(data, np.uint8)
+ data = cv2.imdecode(data, cv2.IMREAD_COLOR)
+ _, data = cv2.imencode(".jpg", data, [int(cv2.IMWRITE_JPEG_QUALITY), 75])
+ data = base64.b64encode(data)
+ data = data.decode("ascii")
+
+ return {"elapsed": elapsed, "screenshot": data}
+
+
+@app.route("/check-skland")
+@require_token
+def test_skland():
+ from arknights_mower.solvers.skland import SKLand
+
+ return SKLand().test_connect()
+
+
+@app.route("/task", methods=["GET", "POST"])
+def get_count():
+ from arknights_mower.__main__ import base_scheduler
+ from arknights_mower.data import agent_list
+ from arknights_mower.utils.operators import SkillUpgradeSupport
+ from arknights_mower.utils.scheduler_task import SchedulerTask, TaskTypes
+
+ if request.method == "POST":
+ try:
+ req = request.json
+ task = req["task"]
+ logger.debug(f"收到新增任务请求:{req}")
+ if base_scheduler and mower_thread.is_alive():
+ # if not base_scheduler.sleeping:
+ # raise Exception("只能在休息时间添加")
+ if task:
+ utc_time = datetime.datetime.strptime(
+ task["time"], "%Y-%m-%dT%H:%M:%S.%f%z"
+ )
+ task_time = (
+ utc_time.replace(tzinfo=pytz.utc)
+ .astimezone(get_localzone())
+ .replace(tzinfo=None)
+ )
+ new_task = SchedulerTask(
+ time=task_time,
+ task_plan=task["plan"],
+ task_type=task["task_type"],
+ meta_data=task["meta_data"],
+ )
+ if base_scheduler.find_next_task(
+ compare_time=task_time, compare_type="="
+ ):
+ raise Exception("找到同时间任务请勿重复添加")
+ if new_task.type == TaskTypes.SKILL_UPGRADE:
+ supports = []
+ for s in req["upgrade_support"]:
+ if (
+ s["name"] not in agent_list
+ or s["swap_name"] not in agent_list
+ ):
+ raise Exception("干员名不正确")
+ supports.append(
+ SkillUpgradeSupport(
+ name=s["name"],
+ skill_level=s["skill_level"],
+ efficiency=s["efficiency"],
+ match=s["match"],
+ swap_name=s["swap_name"],
+ )
+ )
+ if len(supports) == 0:
+ raise Exception("请添加专精工具人")
+ base_scheduler.op_data.skill_upgrade_supports = supports
+ logger.error("更新专精工具人完毕")
+ base_scheduler.tasks.append(new_task)
+ logger.debug(f"成功:{str(new_task)}")
+ return "添加任务成功!"
+ raise Exception("添加任务失败!!")
+ except Exception as e:
+ logger.exception(f"添加任务失败:{str(e)}")
+ return str(e)
+ else:
+ if base_scheduler and mower_thread and mower_thread.is_alive():
+ from jsonpickle import encode
+
+ return [
+ json.loads(
+ encode(
+ i,
+ unpicklable=False,
+ )
+ )
+ for i in base_scheduler.tasks
+ ]
+ else:
+ return []
diff --git a/setup.py b/setup.py
index d21dfd15d..0587b62af 100644
--- a/setup.py
+++ b/setup.py
@@ -1,39 +1,49 @@
+from pathlib import Path
+
import setuptools
+
import arknights_mower
-from pathlib import Path
-LONG_DESC = Path('README.md').read_text('utf8')
+LONG_DESC = Path("README.md").read_text("utf8")
VERSION = arknights_mower.__version__
-
setuptools.setup(
- name='arknights_mower',
+ name="arknights_mower",
version=VERSION,
- author='Konano',
- author_email='w@nano.ac',
- description='Arknights Helper based on ADB and Python',
+ author="Konano",
+ author_email="w@nano.ac",
+ description="Arknights Helper based on ADB and Python",
long_description=LONG_DESC,
- long_description_content_type='text/markdown',
- url='https://github.com/Konano/arknights-mower',
+ long_description_content_type="text/markdown",
+ url="https://github.com/Konano/arknights-mower",
packages=setuptools.find_packages(),
install_requires=[
- 'colorlog', 'opencv_python', 'matplotlib', 'numpy', 'scikit_image==0.18.3', 'scikit_learn>=1',
- 'onnxruntime', 'pyclipper', 'shapely', 'tornado', 'requests', 'ruamel.yaml', 'schedule'
+ "colorlog",
+ "opencv_python",
+ "matplotlib",
+ "numpy",
+ "scikit_image==0.18.3",
+ "scikit_learn>=1",
+ "onnxruntime",
+ "pyclipper",
+ "shapely",
+ "tornado",
+ "requests",
+ "ruamel.yaml",
+ "schedule",
],
include_package_data=True,
- entry_points={'console_scripts': [
- 'arknights-mower=arknights_mower.__main__:main'
- ]},
+ entry_points={"console_scripts": ["arknights-mower=arknights_mower.__main__:main"]},
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Topic :: Games/Entertainment',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
+ "Development Status :: 5 - Production/Stable",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Topic :: Games/Entertainment",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
],
)
diff --git a/ui/.env b/ui/.env
new file mode 100644
index 000000000..04562a055
--- /dev/null
+++ b/ui/.env
@@ -0,0 +1 @@
+VITE_HTTP_URL=""
diff --git a/ui/.env.development b/ui/.env.development
new file mode 100644
index 000000000..b53c7112f
--- /dev/null
+++ b/ui/.env.development
@@ -0,0 +1 @@
+VITE_HTTP_URL="http://localhost:8000"
\ No newline at end of file
diff --git a/ui/.eslintrc.cjs b/ui/.eslintrc.cjs
new file mode 100644
index 000000000..b64731a0f
--- /dev/null
+++ b/ui/.eslintrc.cjs
@@ -0,0 +1,14 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+ root: true,
+ 'extends': [
+ 'plugin:vue/vue3-essential',
+ 'eslint:recommended',
+ '@vue/eslint-config-prettier/skip-formatting'
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest'
+ }
+}
diff --git a/ui/.gitignore b/ui/.gitignore
new file mode 100644
index 000000000..38adffa64
--- /dev/null
+++ b/ui/.gitignore
@@ -0,0 +1,28 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/ui/.prettierignore b/ui/.prettierignore
new file mode 100644
index 000000000..eed5d0d1a
--- /dev/null
+++ b/ui/.prettierignore
@@ -0,0 +1,3 @@
+# 各种自动生成的json
+src/pages/basement_skill/*.json
+src/pages/stage_data/event_data.json
\ No newline at end of file
diff --git a/ui/.prettierrc.json b/ui/.prettierrc.json
new file mode 100644
index 000000000..91b286aca
--- /dev/null
+++ b/ui/.prettierrc.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": false,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "printWidth": 100,
+ "trailingComma": "none",
+ "endOfLine": "auto"
+}
diff --git a/ui/.vscode/extensions.json b/ui/.vscode/extensions.json
new file mode 100644
index 000000000..c0a6e5a48
--- /dev/null
+++ b/ui/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
+}
diff --git a/ui/README.md b/ui/README.md
new file mode 100644
index 000000000..1eb166dbc
--- /dev/null
+++ b/ui/README.md
@@ -0,0 +1,84 @@
+# Mower Web UI
+
+Mower 的新界面。短期目标是 Mower 继续保持桌面应用的形态,界面运行在 WebView 中,取代原来的界面。未来考虑支持在浏览器中运行界面。代码也为在其它网站上展示、编辑 Mower 的排班表提供了可能。
+
+本仓库仅包含前端代码,运行需要后端代码支持。
+
+## 开发
+
+开发时需要分别运行后端和前端。
+
+### 后端
+
+需要 Python 3.8 或 3.9。
+
+后端代码在 [ArkMowers/arknights-mower](https://github.com/ArkMowers/arknights-mower) 仓库的 `dev_shawn` 分支中。
+
+安装依赖:
+
+```bash
+pip install -r requirements.txt
+pip install Flask flask-cors flask-sock pywebview
+```
+
+运行后端:
+
+```bash
+flask --app server run --port=8000 --reload
+```
+
+### 前端
+
+需要 Node.js 16。
+
+安装依赖:
+
+```bash
+npm install
+```
+
+运行前端的开发服务器:
+
+```bash
+npm run dev
+```
+
+根据输出提示,在浏览器中打开窗口即可。
+
+在开发时,前端默认会访问本地 `8000` 端口以连接后端。可以建立 `.env.development.local` 文件,通过 `VITE_HTTP_URL` 指定连接其它地址。例如连接本地的 5000 端口:
+
+```plaintext
+VITE_HTTP_URL="http://localhost:5000"
+```
+
+## 构建与测试
+
+此时无需运行前端的开发服务器,前端构建生产版本的静态文件:
+
+```bash
+npm run build
+```
+
+将生成的 `dist` 文件夹复制到 `arknights-mower` 的目录中。此时运行后端:
+
+```运行
+flask --app server run --port=8000
+```
+
+直接在浏览器中打开 ,就能看到前端了;运行 `./webview_ui.py`,也能在 WebView 窗口中看到前端。
+
+## 打包
+
+安装依赖:
+
+```bash
+pip install pyinstaller
+```
+
+使用 `pyinstaller` 打包:
+
+```bash
+pyinstaller menu.spec
+```
+
+生成的 `mower.exe` 在 `dist` 文件夹中。
diff --git a/ui/auto-imports.d.ts b/ui/auto-imports.d.ts
new file mode 100644
index 000000000..70073592c
--- /dev/null
+++ b/ui/auto-imports.d.ts
@@ -0,0 +1,70 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+export {}
+declare global {
+ const EffectScope: typeof import('vue')['EffectScope']
+ const computed: typeof import('vue')['computed']
+ const createApp: typeof import('vue')['createApp']
+ const customRef: typeof import('vue')['customRef']
+ const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+ const defineComponent: typeof import('vue')['defineComponent']
+ const effectScope: typeof import('vue')['effectScope']
+ const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+ const getCurrentScope: typeof import('vue')['getCurrentScope']
+ const h: typeof import('vue')['h']
+ const inject: typeof import('vue')['inject']
+ const isProxy: typeof import('vue')['isProxy']
+ const isReactive: typeof import('vue')['isReactive']
+ const isReadonly: typeof import('vue')['isReadonly']
+ const isRef: typeof import('vue')['isRef']
+ const markRaw: typeof import('vue')['markRaw']
+ const nextTick: typeof import('vue')['nextTick']
+ const onActivated: typeof import('vue')['onActivated']
+ const onBeforeMount: typeof import('vue')['onBeforeMount']
+ const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+ const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+ const onDeactivated: typeof import('vue')['onDeactivated']
+ const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+ const onMounted: typeof import('vue')['onMounted']
+ const onRenderTracked: typeof import('vue')['onRenderTracked']
+ const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+ const onScopeDispose: typeof import('vue')['onScopeDispose']
+ const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+ const onUnmounted: typeof import('vue')['onUnmounted']
+ const onUpdated: typeof import('vue')['onUpdated']
+ const provide: typeof import('vue')['provide']
+ const reactive: typeof import('vue')['reactive']
+ const readonly: typeof import('vue')['readonly']
+ const ref: typeof import('vue')['ref']
+ const resolveComponent: typeof import('vue')['resolveComponent']
+ const shallowReactive: typeof import('vue')['shallowReactive']
+ const shallowReadonly: typeof import('vue')['shallowReadonly']
+ const shallowRef: typeof import('vue')['shallowRef']
+ const toRaw: typeof import('vue')['toRaw']
+ const toRef: typeof import('vue')['toRef']
+ const toRefs: typeof import('vue')['toRefs']
+ const toValue: typeof import('vue')['toValue']
+ const triggerRef: typeof import('vue')['triggerRef']
+ const unref: typeof import('vue')['unref']
+ const useAttrs: typeof import('vue')['useAttrs']
+ const useCssModule: typeof import('vue')['useCssModule']
+ const useCssVars: typeof import('vue')['useCssVars']
+ const useDialog: typeof import('naive-ui')['useDialog']
+ const useLoadingBar: typeof import('naive-ui')['useLoadingBar']
+ const useMessage: typeof import('naive-ui')['useMessage']
+ const useNotification: typeof import('naive-ui')['useNotification']
+ const useSlots: typeof import('vue')['useSlots']
+ const watch: typeof import('vue')['watch']
+ const watchEffect: typeof import('vue')['watchEffect']
+ const watchPostEffect: typeof import('vue')['watchPostEffect']
+ const watchSyncEffect: typeof import('vue')['watchSyncEffect']
+}
+// for type re-export
+declare global {
+ // @ts-ignore
+ export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+ import('vue')
+}
diff --git a/ui/components.d.ts b/ui/components.d.ts
new file mode 100644
index 000000000..4cb96d2d5
--- /dev/null
+++ b/ui/components.d.ts
@@ -0,0 +1,95 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {}
+
+declare module 'vue' {
+ export interface GlobalComponents {
+ Buffer: typeof import('./src/components/buffer.vue')['default']
+ Bufferinfo: typeof import('./src/components/bufferinfo.vue')['default']
+ Clue: typeof import('./src/components/Clue.vue')['default']
+ DailyMission: typeof import('./src/components/DailyMission.vue')['default']
+ Depotswitch: typeof import('./src/components/Depotswitch.vue')['default']
+ DropDown: typeof import('./src/components/DropDown.vue')['default']
+ Email: typeof import('./src/components/Email.vue')['default']
+ HelpText: typeof import('./src/components/HelpText.vue')['default']
+ LongTasks: typeof import('./src/components/LongTasks.vue')['default']
+ MaaBasic: typeof import('./src/components/MaaBasic.vue')['default']
+ MaaRogue: typeof import('./src/components/MaaRogue.vue')['default']
+ MaaSss: typeof import('./src/components/MaaSss.vue')['default']
+ MaaWeekly: typeof import('./src/components/MaaWeekly.vue')['default']
+ MaaWeeklyNew: typeof import('./src/components/MaaWeeklyNew.vue')['default']
+ NA: typeof import('naive-ui')['NA']
+ NAlert: typeof import('naive-ui')['NAlert']
+ NAutoComplete: typeof import('naive-ui')['NAutoComplete']
+ NAvatar: typeof import('naive-ui')['NAvatar']
+ NButton: typeof import('naive-ui')['NButton']
+ NButtonGroup: typeof import('naive-ui')['NButtonGroup']
+ NCard: typeof import('naive-ui')['NCard']
+ NCheckbox: typeof import('naive-ui')['NCheckbox']
+ NCode: typeof import('naive-ui')['NCode']
+ NConfigProvider: typeof import('naive-ui')['NConfigProvider']
+ NDatePicker: typeof import('naive-ui')['NDatePicker']
+ NDialogProvider: typeof import('naive-ui')['NDialogProvider']
+ NDivider: typeof import('naive-ui')['NDivider']
+ NDropdown: typeof import('naive-ui')['NDropdown']
+ NDynamicInput: typeof import('naive-ui')['NDynamicInput']
+ NDynamicTags: typeof import('naive-ui')['NDynamicTags']
+ NFlex: typeof import('naive-ui')['NFlex']
+ NForm: typeof import('naive-ui')['NForm']
+ NFormItem: typeof import('naive-ui')['NFormItem']
+ NFormItemGi: typeof import('naive-ui')['NFormItemGi']
+ NGi: typeof import('naive-ui')['NGi']
+ NGlobalStyle: typeof import('naive-ui')['NGlobalStyle']
+ NGrid: typeof import('naive-ui')['NGrid']
+ NH2: typeof import('naive-ui')['NH2']
+ NIcon: typeof import('naive-ui')['NIcon']
+ NImage: typeof import('naive-ui')['NImage']
+ NInput: typeof import('naive-ui')['NInput']
+ NInputNumber: typeof import('naive-ui')['NInputNumber']
+ NLayout: typeof import('naive-ui')['NLayout']
+ NLayoutContent: typeof import('naive-ui')['NLayoutContent']
+ NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
+ NLayoutSider: typeof import('naive-ui')['NLayoutSider']
+ NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
+ NLog: typeof import('naive-ui')['NLog']
+ NMenu: typeof import('naive-ui')['NMenu']
+ NMessageProvider: typeof import('naive-ui')['NMessageProvider']
+ NModal: typeof import('naive-ui')['NModal']
+ NRadio: typeof import('naive-ui')['NRadio']
+ NRadioButton: typeof import('naive-ui')['NRadioButton']
+ NRadioGroup: typeof import('naive-ui')['NRadioGroup']
+ NScrollbar: typeof import('naive-ui')['NScrollbar']
+ NSelect: typeof import('naive-ui')['NSelect']
+ NSlider: typeof import('naive-ui')['NSlider']
+ NSpace: typeof import('naive-ui')['NSpace']
+ NSwitch: typeof import('naive-ui')['NSwitch']
+ NTab: typeof import('naive-ui')['NTab']
+ NTable: typeof import('naive-ui')['NTable']
+ NTabs: typeof import('naive-ui')['NTabs']
+ NTag: typeof import('naive-ui')['NTag']
+ NTd: typeof import('naive-ui')['NTd']
+ NThing: typeof import('naive-ui')['NThing']
+ NTimePicker: typeof import('naive-ui')['NTimePicker']
+ NTooltip: typeof import('naive-ui')['NTooltip']
+ NTr: typeof import('naive-ui')['NTr']
+ NTransfer: typeof import('naive-ui')['NTransfer']
+ NUpload: typeof import('naive-ui')['NUpload']
+ NVirtualList: typeof import('naive-ui')['NVirtualList']
+ NWatermark: typeof import('naive-ui')['NWatermark']
+ PlanEditor: typeof import('./src/components/PlanEditor.vue')['default']
+ ReclamationAlgorithm: typeof import('./src/components/ReclamationAlgorithm.vue')['default']
+ Recruit: typeof import('./src/components/Recruit.vue')['default']
+ RouterLink: typeof import('vue-router')['RouterLink']
+ RouterView: typeof import('vue-router')['RouterView']
+ SecretFront: typeof import('./src/components/SecretFront.vue')['default']
+ SKLand: typeof import('./src/components/SKLand.vue')['default']
+ SlickOperatorSelect: typeof import('./src/components/SlickOperatorSelect.vue')['default']
+ TaskDialog: typeof import('./src/components/TaskDialog.vue')['default']
+ TriggerDialog: typeof import('./src/components/TriggerDialog.vue')['default']
+ TriggerEditor: typeof import('./src/components/TriggerEditor.vue')['default']
+ TriggerString: typeof import('./src/components/TriggerString.vue')['default']
+ }
+}
diff --git a/ui/index.html b/ui/index.html
new file mode 100644
index 000000000..3b70486d5
--- /dev/null
+++ b/ui/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Mower WebUI in Browser
+
+
+
+
+
+
diff --git a/ui/jsconfig.json b/ui/jsconfig.json
new file mode 100644
index 000000000..b18e5bc35
--- /dev/null
+++ b/ui/jsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "allowJs": true
+ }
+}
diff --git a/ui/manager/MowerManager.vue b/ui/manager/MowerManager.vue
new file mode 100644
index 000000000..11aed0199
--- /dev/null
+++ b/ui/manager/MowerManager.vue
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ instance.path || '请选择该实例配置文件的保存路径' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加实例
+
+
+
+
+
diff --git a/ui/manager/index.html b/ui/manager/index.html
new file mode 100644
index 000000000..2cc145a85
--- /dev/null
+++ b/ui/manager/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Mower Manager
+
+
+
+
+
+
diff --git a/ui/manager/main.js b/ui/manager/main.js
new file mode 100644
index 000000000..e4c61b2ed
--- /dev/null
+++ b/ui/manager/main.js
@@ -0,0 +1,5 @@
+import { createApp } from 'vue'
+import MowerManager from './MowerManager.vue'
+
+const app = createApp(MowerManager)
+app.mount('#app')
diff --git a/ui/package-lock.json b/ui/package-lock.json
new file mode 100644
index 000000000..b1454e3e5
--- /dev/null
+++ b/ui/package-lock.json
@@ -0,0 +1,7796 @@
+{
+ "name": "mower-ui",
+ "version": "0.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "mower-ui",
+ "version": "0.0.0",
+ "dependencies": {
+ "@vicons/fluent": "^0.12.0",
+ "@vicons/ionicons4": "^0.12.0",
+ "@vicons/ionicons5": "^0.12.0",
+ "@vicons/material": "^0.12.0",
+ "@vicons/tabler": "^0.12.0",
+ "axios": "^1.6.8",
+ "bowser": "^2.11.0",
+ "chart.js": "^4.4.2",
+ "chartjs-adapter-luxon": "^1.3.1",
+ "chartjs-adapter-moment": "^1.0.1",
+ "chartjs-plugin-datalabels": "^2.2.0",
+ "echarts": "^5.5.0",
+ "file-saver": "^2.0.5",
+ "highlight.js": "^11.9.0",
+ "html-to-image": "^1.11.11",
+ "katex": "^0.16.10",
+ "luxon": "^3.4.4",
+ "moment": "^2.30.1",
+ "pinia": "^2.1.7",
+ "pinyin-pro": "^3.19.6",
+ "prettier": "^3.3.3",
+ "reconnecting-websocket": "^4.4.0",
+ "vue": "^3.4.27",
+ "vue-axios": "^3.5.2",
+ "vue-chartjs": "^5.3.0",
+ "vue-echarts": "^6.6.9",
+ "vue-router": "^4.3.0",
+ "vue-slicksort": "^2.0.5"
+ },
+ "devDependencies": {
+ "@rushstack/eslint-patch": "^1.10.1",
+ "@vicons/carbon": "^0.12.0",
+ "@vicons/fa": "^0.12.0",
+ "@vitejs/plugin-vue": "^5.0.4",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "@vue/eslint-config-prettier": "^9.0.0",
+ "eslint": "^8.57.0",
+ "eslint-plugin-vue": "^9.24.0",
+ "naive-ui": "^2.38.1",
+ "sass": "^1.74.1",
+ "typescript": "^5.4.4",
+ "unplugin-auto-import": "^0.17.5",
+ "unplugin-vue-components": "^0.26.0",
+ "vite": "^5.2.8",
+ "vite-plugin-inspect": "^0.8.3"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@antfu/utils": {
+ "version": "0.7.7",
+ "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.7.tgz",
+ "integrity": "sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==",
+ "dev": true
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz",
+ "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.23.4",
+ "chalk": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.23.5.tgz",
+ "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.23.6.tgz",
+ "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-compilation-targets": "^7.23.6",
+ "@babel/helper-module-transforms": "^7.23.3",
+ "@babel/helpers": "^7.23.6",
+ "@babel/parser": "^7.23.6",
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.6",
+ "@babel/types": "^7.23.6",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.23.6.tgz",
+ "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.6",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
+ "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+ "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.23.5",
+ "@babel/helper-validator-option": "^7.23.5",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz",
+ "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-member-expression-to-functions": "^7.23.0",
+ "@babel/helper-optimise-call-expression": "^7.22.5",
+ "@babel/helper-replace-supers": "^7.22.20",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
+ "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
+ "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+ "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-validator-identifier": "^7.22.20"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
+ "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
+ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz",
+ "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-member-expression-to-functions": "^7.22.15",
+ "@babel/helper-optimise-call-expression": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
+ "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.22.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
+ "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+ "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.23.6.tgz",
+ "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.6",
+ "@babel/types": "^7.23.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz",
+ "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.24.4",
+ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.4.tgz",
+ "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz",
+ "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz",
+ "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz",
+ "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-create-class-features-plugin": "^7.23.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "@babel/plugin-syntax-typescript": "^7.23.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.6.tgz",
+ "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+ "dev": true,
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.23.6.tgz",
+ "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.6",
+ "@babel/types": "^7.23.6",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.23.6.tgz",
+ "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.23.4",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@css-render/plugin-bem": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.12.tgz",
+ "integrity": "sha512-Lq2jSOZn+wYQtsyaFj6QRz2EzAnd3iW5fZeHO1WSXQdVYwvwGX0ZiH3X2JQgtgYLT1yeGtrwrqJdNdMEUD2xTw==",
+ "dev": true,
+ "peerDependencies": {
+ "css-render": "~0.15.12"
+ }
+ },
+ "node_modules/@css-render/vue3-ssr": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmjs.org/@css-render/vue3-ssr/-/vue3-ssr-0.15.12.tgz",
+ "integrity": "sha512-AQLGhhaE0F+rwybRCkKUdzBdTEM/5PZBYy+fSYe1T9z9+yxMuV/k7ZRqa4M69X+EI1W8pa4kc9Iq2VjQkZx4rg==",
+ "dev": true,
+ "peerDependencies": {
+ "vue": "^3.0.11"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "dev": true
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+ "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
+ "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@juggle/resize-observer": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
+ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
+ "dev": true
+ },
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.25",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
+ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
+ "dev": true
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
+ "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
+ "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
+ "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
+ "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
+ "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
+ "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
+ "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
+ "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
+ "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
+ "cpu": [
+ "ppc64le"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
+ "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
+ "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
+ "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
+ "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
+ "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
+ "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
+ "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rushstack/eslint-patch": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",
+ "integrity": "sha512-S3Kq8e7LqxkA9s7HKLqXGTGck1uwis5vAXan3FnU5yw1Ec5hsSGnq4s/UCaSqABPOnOTg7zASLyst7+ohgWexg==",
+ "dev": true
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "node_modules/@types/katex": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmmirror.com/@types/katex/-/katex-0.16.7.tgz",
+ "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==",
+ "dev": true
+ },
+ "node_modules/@types/lodash": {
+ "version": "4.14.202",
+ "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
+ "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+ "dev": true
+ },
+ "node_modules/@types/lodash-es": {
+ "version": "4.17.12",
+ "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+ "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/@vicons/carbon": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@vicons/carbon/-/carbon-0.12.0.tgz",
+ "integrity": "sha512-kCOgr/ZOhZzoiFLJ8pwxMa2TMxrkCUOA22qExPabus35F4+USqzcsxaPoYtqRd9ROOYiHrSqwapak/ywF0D9bg==",
+ "dev": true
+ },
+ "node_modules/@vicons/fa": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@vicons/fa/-/fa-0.12.0.tgz",
+ "integrity": "sha512-g2PIeJLsTHUjt6bK63LxqC0uYQB7iu+xViJOxvp1s8b9/akpXVPVWjDTTsP980/0KYyMMe4U7F/aUo7wY+MsXA==",
+ "dev": true
+ },
+ "node_modules/@vicons/fluent": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/fluent/-/fluent-0.12.0.tgz",
+ "integrity": "sha512-ATCiqPuiJ6RI5GBlD3BIpZ9Xw4MsCA4RpI5oR6MCti4quS4mX1Gp6N74FCzw7lgOj+80rV4HMKhZTVInwimpVQ=="
+ },
+ "node_modules/@vicons/ionicons4": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/ionicons4/-/ionicons4-0.12.0.tgz",
+ "integrity": "sha512-i+7YwlpCrqD6m9esbZLy1bpVQlh4CKugtS3OzgfNw6BLTQQK6HT7drktaJgcESj/BTr4avdNbAtMQXx56wSVMg=="
+ },
+ "node_modules/@vicons/ionicons5": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz",
+ "integrity": "sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA=="
+ },
+ "node_modules/@vicons/material": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/material/-/material-0.12.0.tgz",
+ "integrity": "sha512-chv1CYAl8P32P3Ycwgd5+vw/OFNc2mtkKdb1Rw4T5IJmKy6GVDsoUKV3N2l208HATn7CCQphZtuPDdsm7K2kmA=="
+ },
+ "node_modules/@vicons/tabler": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/tabler/-/tabler-0.12.0.tgz",
+ "integrity": "sha512-3+wUFuxb7e8OzZ8Wryct1pzfA2vyoF4lwW98O9s27ZrfCGaJGNmqG+q8A7vQ92Mf+COCgxpK+rhNPTtTvaU6qw=="
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
+ "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@vitejs/plugin-vue-jsx": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz",
+ "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.23.3",
+ "@babel/plugin-transform-typescript": "^7.23.3",
+ "@vue/babel-plugin-jsx": "^1.1.5"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.0.0 || ^5.0.0",
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/@vue/babel-helper-vue-transform-on": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.1.5.tgz",
+ "integrity": "sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==",
+ "dev": true
+ },
+ "node_modules/@vue/babel-plugin-jsx": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.5.tgz",
+ "integrity": "sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.22.5",
+ "@babel/plugin-syntax-jsx": "^7.22.5",
+ "@babel/template": "^7.22.5",
+ "@babel/traverse": "^7.22.5",
+ "@babel/types": "^7.22.5",
+ "@vue/babel-helper-vue-transform-on": "^1.1.5",
+ "camelcase": "^6.3.0",
+ "html-tags": "^3.3.1",
+ "svg-tags": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+ "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
+ "dependencies": {
+ "@babel/parser": "^7.24.4",
+ "@vue/shared": "3.4.27",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+ "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
+ "dependencies": {
+ "@vue/compiler-core": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+ "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
+ "dependencies": {
+ "@babel/parser": "^7.24.4",
+ "@vue/compiler-core": "3.4.27",
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.10",
+ "postcss": "^8.4.38",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+ "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
+ "dependencies": {
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+ "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+ },
+ "node_modules/@vue/eslint-config-prettier": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
+ "integrity": "sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-prettier": "^5.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">= 8.0.0",
+ "prettier": ">= 3.0.0"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.27.tgz",
+ "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
+ "dependencies": {
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+ "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
+ "dependencies": {
+ "@vue/reactivity": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+ "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
+ "dependencies": {
+ "@vue/runtime-core": "3.4.27",
+ "@vue/shared": "3.4.27",
+ "csstype": "^3.1.3"
+ }
+ },
+ "node_modules/@vue/runtime-dom/node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+ "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27"
+ },
+ "peerDependencies": {
+ "vue": "3.4.27"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/async-validator": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+ "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
+ "dev": true
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "node_modules/bowser": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmmirror.com/bowser/-/bowser-2.11.0.tgz",
+ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.22.2",
+ "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.2.tgz",
+ "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+ "dev": true,
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001565",
+ "electron-to-chromium": "^1.4.601",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "dependencies": {
+ "run-applescript": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001570",
+ "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz",
+ "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==",
+ "dev": true
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chart.js": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.4.2.tgz",
+ "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
+ "node_modules/chartjs-adapter-luxon": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmmirror.com/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.3.1.tgz",
+ "integrity": "sha512-yxHov3X8y+reIibl1o+j18xzrcdddCLqsXhriV2+aQ4hCR66IYFchlRXUvrJVoxglJ380pgytU7YWtoqdIgqhg==",
+ "peerDependencies": {
+ "chart.js": ">=3.0.0",
+ "luxon": ">=1.0.0"
+ }
+ },
+ "node_modules/chartjs-adapter-moment": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
+ "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
+ "peerDependencies": {
+ "chart.js": ">=3.0.0",
+ "moment": "^2.10.2"
+ }
+ },
+ "node_modules/chartjs-plugin-datalabels": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+ "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+ "peerDependencies": {
+ "chart.js": ">=3.0.0"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-render": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
+ "integrity": "sha512-eWzS66patiGkTTik+ipO9qNGZ+uNuGyTmnz6/+EJIiFg8+3yZRpnMwgFo8YdXhQRsiePzehnusrxVvugNjXzbw==",
+ "dev": true,
+ "dependencies": {
+ "@emotion/hash": "~0.8.0",
+ "csstype": "~3.0.5"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
+ "dev": true
+ },
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ }
+ },
+ "node_modules/date-fns-tz": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/date-fns-tz/-/date-fns-tz-2.0.0.tgz",
+ "integrity": "sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==",
+ "dev": true,
+ "peerDependencies": {
+ "date-fns": ">=2.0.0"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/default-browser": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
+ "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
+ "dev": true,
+ "dependencies": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
+ "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/echarts": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz",
+ "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
+ "dependencies": {
+ "tslib": "2.3.0",
+ "zrender": "5.5.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.614",
+ "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz",
+ "integrity": "sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-stack-parser-es": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-0.1.1.tgz",
+ "integrity": "sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.20.2.tgz",
+ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+ "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.6"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-vue": {
+ "version": "9.24.0",
+ "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz",
+ "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "globals": "^13.24.0",
+ "natural-compare": "^1.4.0",
+ "nth-check": "^2.1.1",
+ "postcss-selector-parser": "^6.0.15",
+ "semver": "^7.6.0",
+ "vue-eslint-parser": "^9.4.2",
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-vue/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-vue/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-vue/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-vue/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/evtd": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/evtd/-/evtd-0.2.4.tgz",
+ "integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
+ "dev": true
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.9.tgz",
+ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz",
+ "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.9.0",
+ "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz",
+ "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/html-tags": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.3.1.tgz",
+ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/html-to-image": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz",
+ "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
+ },
+ "node_modules/ignore": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.0.tgz",
+ "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.4.tgz",
+ "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+ "dev": true
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
+ "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+ "dev": true,
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
+ "dev": true
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.16.10",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
+ "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/local-pkg": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.3.tgz",
+ "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/luxon": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz",
+ "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.10",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz",
+ "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mlly": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.6.1.tgz",
+ "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.11.3",
+ "pathe": "^1.1.2",
+ "pkg-types": "^1.0.3",
+ "ufo": "^1.3.2"
+ }
+ },
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
+ "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/naive-ui": {
+ "version": "2.38.1",
+ "resolved": "https://registry.npmjs.org/naive-ui/-/naive-ui-2.38.1.tgz",
+ "integrity": "sha512-AnU1FQ7K/CbhguAX++V4kCFjk7h7RvWt4nvZPRjORMpq+fUIlzD+EcQ5Cv1VqDloNF8+eMv4Akc2Ogacc9S+5A==",
+ "dev": true,
+ "dependencies": {
+ "@css-render/plugin-bem": "^0.15.12",
+ "@css-render/vue3-ssr": "^0.15.12",
+ "@types/katex": "^0.16.2",
+ "@types/lodash": "^4.14.198",
+ "@types/lodash-es": "^4.17.9",
+ "async-validator": "^4.2.5",
+ "css-render": "^0.15.12",
+ "csstype": "^3.1.3",
+ "date-fns": "^2.30.0",
+ "date-fns-tz": "^2.0.0",
+ "evtd": "^0.2.4",
+ "highlight.js": "^11.8.0",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "seemly": "^0.3.8",
+ "treemate": "^0.3.11",
+ "vdirs": "^0.1.8",
+ "vooks": "^0.2.12",
+ "vueuc": "^0.4.58"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/naive-ui/node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/open": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz",
+ "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==",
+ "dev": true,
+ "dependencies": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "is-wsl": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true
+ },
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/pinia": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
+ "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+ "dependencies": {
+ "@vue/devtools-api": "^6.5.0",
+ "vue-demi": ">=0.14.5"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.4.0",
+ "typescript": ">=4.4.4",
+ "vue": "^2.6.14 || ^3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinia/node_modules/vue-demi": {
+ "version": "0.14.6",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz",
+ "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinyin-pro": {
+ "version": "3.19.6",
+ "resolved": "https://registry.npmmirror.com/pinyin-pro/-/pinyin-pro-3.19.6.tgz",
+ "integrity": "sha512-oWb34orr12+DjXf6gtGMB+gIpjRi7DZzyJE66ultbmNzVhpimM/utGtMI8GcbOy/lb26Ph/nogwNYriRPu+SGQ=="
+ },
+ "node_modules/pkg-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz",
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "dev": true,
+ "dependencies": {
+ "jsonc-parser": "^3.2.0",
+ "mlly": "^1.2.0",
+ "pathe": "^1.1.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/reconnecting-websocket": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz",
+ "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng=="
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "dev": true
+ },
+ "node_modules/resize-detector": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
+ "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.14.0.tgz",
+ "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.14.0",
+ "@rollup/rollup-android-arm64": "4.14.0",
+ "@rollup/rollup-darwin-arm64": "4.14.0",
+ "@rollup/rollup-darwin-x64": "4.14.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.14.0",
+ "@rollup/rollup-linux-arm64-musl": "4.14.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.14.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.14.0",
+ "@rollup/rollup-linux-x64-gnu": "4.14.0",
+ "@rollup/rollup-linux-x64-musl": "4.14.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.14.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.14.0",
+ "@rollup/rollup-win32-x64-msvc": "4.14.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-applescript": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
+ "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/sass": {
+ "version": "1.74.1",
+ "resolved": "https://registry.npmmirror.com/sass/-/sass-1.74.1.tgz",
+ "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/scule": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
+ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
+ "dev": true
+ },
+ "node_modules/seemly": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/seemly/-/seemly-0.3.8.tgz",
+ "integrity": "sha512-MW8Qs6vbzo0pHmDpFSYPna+lwpZ6Zk1ancbajw/7E8TKtHdV+1DfZZD+kKJEhG/cAoB/i+LiT+5msZOqj0DwRA==",
+ "dev": true
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sirv": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+ "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
+ "dev": true,
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-literal": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-1.3.0.tgz",
+ "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/svg-tags": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz",
+ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+ "dev": true
+ },
+ "node_modules/synckit": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.8.8.tgz",
+ "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/synckit/node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/treemate": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz",
+ "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
+ "dev": true
+ },
+ "node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.4.4.tgz",
+ "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
+ "devOptional": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.5.3.tgz",
+ "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
+ "dev": true
+ },
+ "node_modules/unimport": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmmirror.com/unimport/-/unimport-3.7.1.tgz",
+ "integrity": "sha512-V9HpXYfsZye5bPPYUgs0Otn3ODS1mDUciaBlXljI4C2fTwfFpvFZRywmlOu943puN9sncxROMZhsZCjNXEpzEQ==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.1.0",
+ "acorn": "^8.11.2",
+ "escape-string-regexp": "^5.0.0",
+ "estree-walker": "^3.0.3",
+ "fast-glob": "^3.3.2",
+ "local-pkg": "^0.5.0",
+ "magic-string": "^0.30.5",
+ "mlly": "^1.4.2",
+ "pathe": "^1.1.1",
+ "pkg-types": "^1.0.3",
+ "scule": "^1.1.1",
+ "strip-literal": "^1.3.0",
+ "unplugin": "^1.5.1"
+ }
+ },
+ "node_modules/unimport/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unimport/node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/unimport/node_modules/local-pkg": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz",
+ "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+ "dev": true,
+ "dependencies": {
+ "mlly": "^1.4.2",
+ "pkg-types": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unplugin": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.10.1.tgz",
+ "integrity": "sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.11.3",
+ "chokidar": "^3.6.0",
+ "webpack-sources": "^3.2.3",
+ "webpack-virtual-modules": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/unplugin-auto-import": {
+ "version": "0.17.5",
+ "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.17.5.tgz",
+ "integrity": "sha512-fHNDkDSxv3PGagX1wmKBYBkgaM4AKAgZmdJw/bxjhNljx9KSXSgHpGfX0MwUrq9qw6q1bhHIZVWyOwoY2koo4w==",
+ "dev": true,
+ "dependencies": {
+ "@antfu/utils": "^0.7.7",
+ "@rollup/pluginutils": "^5.1.0",
+ "fast-glob": "^3.3.2",
+ "local-pkg": "^0.5.0",
+ "magic-string": "^0.30.5",
+ "minimatch": "^9.0.3",
+ "unimport": "^3.7.1",
+ "unplugin": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@nuxt/kit": "^3.2.2",
+ "@vueuse/core": "*"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/kit": {
+ "optional": true
+ },
+ "@vueuse/core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/unplugin-auto-import/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/unplugin-auto-import/node_modules/local-pkg": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz",
+ "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+ "dev": true,
+ "dependencies": {
+ "mlly": "^1.4.2",
+ "pkg-types": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/unplugin-auto-import/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/unplugin-vue-components": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.26.0.tgz",
+ "integrity": "sha512-s7IdPDlnOvPamjunVxw8kNgKNK8A5KM1YpK5j/p97jEKTjlPNrA0nZBiSfAKKlK1gWZuyWXlKL5dk3EDw874LQ==",
+ "dev": true,
+ "dependencies": {
+ "@antfu/utils": "^0.7.6",
+ "@rollup/pluginutils": "^5.0.4",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.1",
+ "local-pkg": "^0.4.3",
+ "magic-string": "^0.30.3",
+ "minimatch": "^9.0.3",
+ "resolve": "^1.22.4",
+ "unplugin": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@babel/parser": "^7.15.8",
+ "@nuxt/kit": "^3.2.2",
+ "vue": "2 || 3"
+ },
+ "peerDependenciesMeta": {
+ "@babel/parser": {
+ "optional": true
+ },
+ "@nuxt/kit": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/unplugin-vue-components/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/unplugin-vue-components/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/vdirs": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/vdirs/-/vdirs-0.1.8.tgz",
+ "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
+ "dev": true,
+ "dependencies": {
+ "evtd": "^0.2.2"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.11"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.2.8",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-5.2.8.tgz",
+ "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.20.1",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-inspect": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-0.8.3.tgz",
+ "integrity": "sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og==",
+ "dev": true,
+ "dependencies": {
+ "@antfu/utils": "^0.7.7",
+ "@rollup/pluginutils": "^5.1.0",
+ "debug": "^4.3.4",
+ "error-stack-parser-es": "^0.1.1",
+ "fs-extra": "^11.2.0",
+ "open": "^10.0.3",
+ "perfect-debounce": "^1.0.0",
+ "picocolors": "^1.0.0",
+ "sirv": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/kit": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vooks": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/vooks/-/vooks-0.2.12.tgz",
+ "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
+ "dev": true,
+ "dependencies": {
+ "evtd": "^0.2.2"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/vue": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.27.tgz",
+ "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
+ "dependencies": {
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-sfc": "3.4.27",
+ "@vue/runtime-dom": "3.4.27",
+ "@vue/server-renderer": "3.4.27",
+ "@vue/shared": "3.4.27"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-axios": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmmirror.com/vue-axios/-/vue-axios-3.5.2.tgz",
+ "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==",
+ "peerDependencies": {
+ "axios": "*",
+ "vue": "^3.0.0 || ^2.0.0"
+ }
+ },
+ "node_modules/vue-chartjs": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/vue-chartjs/-/vue-chartjs-5.3.0.tgz",
+ "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "vue": "^3.0.0-0 || ^2.7.0"
+ }
+ },
+ "node_modules/vue-echarts": {
+ "version": "6.6.9",
+ "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.6.9.tgz",
+ "integrity": "sha512-mojIq3ZvsjabeVmDthhAUDV8Kgf2Rr/X4lV4da7gEFd1fP05gcSJ0j7wa7HQkW5LlFmF2gdCJ8p4Chas6NNIQQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "resize-detector": "^0.3.0",
+ "vue-demi": "^0.13.11"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.5",
+ "@vue/runtime-core": "^3.0.0",
+ "echarts": "^5.4.1",
+ "vue": "^2.6.12 || ^3.1.1"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ },
+ "@vue/runtime-core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-echarts/node_modules/vue-demi": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
+ "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+ "hasInstallScript": true,
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-eslint-parser": {
+ "version": "9.4.2",
+ "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz",
+ "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "eslint-scope": "^7.1.1",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.3.1",
+ "esquery": "^1.4.0",
+ "lodash": "^4.17.21",
+ "semver": "^7.3.6"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/vue-router": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.3.0.tgz",
+ "integrity": "sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==",
+ "dependencies": {
+ "@vue/devtools-api": "^6.5.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
+ "node_modules/vue-slicksort": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/vue-slicksort/-/vue-slicksort-2.0.5.tgz",
+ "integrity": "sha512-fXz1YrNjhUbJK7o0tMk27mIr4pMAZYLSYvtmLazCtfpvz+zafPCn34ILDL8B7hT7WLVZKreYs6JVe5VWymqmzA==",
+ "peerDependencies": {
+ "vue": ">=3.0.0"
+ }
+ },
+ "node_modules/vueuc": {
+ "version": "0.4.58",
+ "resolved": "https://registry.npmjs.org/vueuc/-/vueuc-0.4.58.tgz",
+ "integrity": "sha512-Wnj/N8WbPRSxSt+9ji1jtDHPzda5h2OH/0sFBhvdxDRuyCZbjGg3/cKMaKqEoe+dErTexG2R+i6Q8S/Toq1MYg==",
+ "dev": true,
+ "dependencies": {
+ "@css-render/vue3-ssr": "^0.15.10",
+ "@juggle/resize-observer": "^3.3.1",
+ "css-render": "^0.15.10",
+ "evtd": "^0.2.4",
+ "seemly": "^0.3.6",
+ "vdirs": "^0.1.4",
+ "vooks": "^0.2.4"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.11"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack-virtual-modules": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz",
+ "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
+ "dev": true
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/xml-name-validator": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/zrender": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz",
+ "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
+ "dependencies": {
+ "tslib": "2.3.0"
+ }
+ }
+ },
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true
+ },
+ "@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@antfu/utils": {
+ "version": "0.7.7",
+ "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.7.tgz",
+ "integrity": "sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==",
+ "dev": true
+ },
+ "@babel/code-frame": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz",
+ "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.23.4",
+ "chalk": "^2.4.2"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.23.5.tgz",
+ "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.23.6.tgz",
+ "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-compilation-targets": "^7.23.6",
+ "@babel/helper-module-transforms": "^7.23.3",
+ "@babel/helpers": "^7.23.6",
+ "@babel/parser": "^7.23.6",
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.6",
+ "@babel/types": "^7.23.6",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.23.6.tgz",
+ "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.23.6",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
+ "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+ "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.23.5",
+ "@babel/helper-validator-option": "^7.23.5",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz",
+ "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-member-expression-to-functions": "^7.23.0",
+ "@babel/helper-optimise-call-expression": "^7.22.5",
+ "@babel/helper-replace-supers": "^7.22.20",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "semver": "^6.3.1"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
+ "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.23.0"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
+ "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.15"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+ "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-validator-identifier": "^7.22.20"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
+ "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
+ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
+ "dev": true
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz",
+ "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-member-expression-to-functions": "^7.22.15",
+ "@babel/helper-optimise-call-expression": "^7.22.5"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
+ "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.22.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
+ "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+ "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.23.6.tgz",
+ "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.6",
+ "@babel/types": "^7.23.6"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.23.4",
+ "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz",
+ "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.24.4",
+ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.4.tgz",
+ "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg=="
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz",
+ "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ }
+ },
+ "@babel/plugin-syntax-typescript": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz",
+ "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ }
+ },
+ "@babel/plugin-transform-typescript": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz",
+ "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.22.5",
+ "@babel/helper-create-class-features-plugin": "^7.23.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "@babel/plugin-syntax-typescript": "^7.23.3"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.6.tgz",
+ "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@babel/template": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.23.6.tgz",
+ "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.6",
+ "@babel/types": "^7.23.6",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.23.6.tgz",
+ "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.23.4",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@css-render/plugin-bem": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.12.tgz",
+ "integrity": "sha512-Lq2jSOZn+wYQtsyaFj6QRz2EzAnd3iW5fZeHO1WSXQdVYwvwGX0ZiH3X2JQgtgYLT1yeGtrwrqJdNdMEUD2xTw==",
+ "dev": true,
+ "requires": {}
+ },
+ "@css-render/vue3-ssr": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmjs.org/@css-render/vue3-ssr/-/vue3-ssr-0.15.12.tgz",
+ "integrity": "sha512-AQLGhhaE0F+rwybRCkKUdzBdTEM/5PZBYy+fSYe1T9z9+yxMuV/k7ZRqa4M69X+EI1W8pa4kc9Iq2VjQkZx4rg==",
+ "dev": true,
+ "requires": {}
+ },
+ "@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "dev": true
+ },
+ "@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+ "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true
+ },
+ "@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ }
+ }
+ },
+ "@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
+ "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "@juggle/resize-observer": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
+ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
+ "dev": true
+ },
+ "@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true
+ },
+ "@polka/url": {
+ "version": "1.0.0-next.25",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
+ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
+ "dev": true
+ },
+ "@rollup/pluginutils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
+ "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "@rollup/rollup-android-arm-eabi": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
+ "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-android-arm64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
+ "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-arm64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
+ "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-x64": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
+ "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
+ "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
+ "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-musl": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
+ "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
+ "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
+ "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
+ "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-gnu": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
+ "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-musl": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
+ "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
+ "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
+ "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-x64-msvc": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
+ "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
+ "dev": true,
+ "optional": true
+ },
+ "@rushstack/eslint-patch": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",
+ "integrity": "sha512-S3Kq8e7LqxkA9s7HKLqXGTGck1uwis5vAXan3FnU5yw1Ec5hsSGnq4s/UCaSqABPOnOTg7zASLyst7+ohgWexg==",
+ "dev": true
+ },
+ "@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "@types/katex": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmmirror.com/@types/katex/-/katex-0.16.7.tgz",
+ "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==",
+ "dev": true
+ },
+ "@types/lodash": {
+ "version": "4.14.202",
+ "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
+ "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+ "dev": true
+ },
+ "@types/lodash-es": {
+ "version": "4.17.12",
+ "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+ "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+ "dev": true,
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
+ "@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "@vicons/carbon": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@vicons/carbon/-/carbon-0.12.0.tgz",
+ "integrity": "sha512-kCOgr/ZOhZzoiFLJ8pwxMa2TMxrkCUOA22qExPabus35F4+USqzcsxaPoYtqRd9ROOYiHrSqwapak/ywF0D9bg==",
+ "dev": true
+ },
+ "@vicons/fa": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@vicons/fa/-/fa-0.12.0.tgz",
+ "integrity": "sha512-g2PIeJLsTHUjt6bK63LxqC0uYQB7iu+xViJOxvp1s8b9/akpXVPVWjDTTsP980/0KYyMMe4U7F/aUo7wY+MsXA==",
+ "dev": true
+ },
+ "@vicons/fluent": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/fluent/-/fluent-0.12.0.tgz",
+ "integrity": "sha512-ATCiqPuiJ6RI5GBlD3BIpZ9Xw4MsCA4RpI5oR6MCti4quS4mX1Gp6N74FCzw7lgOj+80rV4HMKhZTVInwimpVQ=="
+ },
+ "@vicons/ionicons4": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/ionicons4/-/ionicons4-0.12.0.tgz",
+ "integrity": "sha512-i+7YwlpCrqD6m9esbZLy1bpVQlh4CKugtS3OzgfNw6BLTQQK6HT7drktaJgcESj/BTr4avdNbAtMQXx56wSVMg=="
+ },
+ "@vicons/ionicons5": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz",
+ "integrity": "sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA=="
+ },
+ "@vicons/material": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/material/-/material-0.12.0.tgz",
+ "integrity": "sha512-chv1CYAl8P32P3Ycwgd5+vw/OFNc2mtkKdb1Rw4T5IJmKy6GVDsoUKV3N2l208HATn7CCQphZtuPDdsm7K2kmA=="
+ },
+ "@vicons/tabler": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/@vicons/tabler/-/tabler-0.12.0.tgz",
+ "integrity": "sha512-3+wUFuxb7e8OzZ8Wryct1pzfA2vyoF4lwW98O9s27ZrfCGaJGNmqG+q8A7vQ92Mf+COCgxpK+rhNPTtTvaU6qw=="
+ },
+ "@vitejs/plugin-vue": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
+ "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "@vitejs/plugin-vue-jsx": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz",
+ "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.23.3",
+ "@babel/plugin-transform-typescript": "^7.23.3",
+ "@vue/babel-plugin-jsx": "^1.1.5"
+ }
+ },
+ "@vue/babel-helper-vue-transform-on": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.1.5.tgz",
+ "integrity": "sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==",
+ "dev": true
+ },
+ "@vue/babel-plugin-jsx": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.5.tgz",
+ "integrity": "sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.22.5",
+ "@babel/plugin-syntax-jsx": "^7.22.5",
+ "@babel/template": "^7.22.5",
+ "@babel/traverse": "^7.22.5",
+ "@babel/types": "^7.22.5",
+ "@vue/babel-helper-vue-transform-on": "^1.1.5",
+ "camelcase": "^6.3.0",
+ "html-tags": "^3.3.1",
+ "svg-tags": "^1.0.0"
+ }
+ },
+ "@vue/compiler-core": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+ "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
+ "requires": {
+ "@babel/parser": "^7.24.4",
+ "@vue/shared": "3.4.27",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "@vue/compiler-dom": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+ "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
+ "requires": {
+ "@vue/compiler-core": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "@vue/compiler-sfc": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+ "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
+ "requires": {
+ "@babel/parser": "^7.24.4",
+ "@vue/compiler-core": "3.4.27",
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.10",
+ "postcss": "^8.4.38",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "@vue/compiler-ssr": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+ "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
+ "requires": {
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "@vue/devtools-api": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+ "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+ },
+ "@vue/eslint-config-prettier": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
+ "integrity": "sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==",
+ "dev": true,
+ "requires": {
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-prettier": "^5.0.0"
+ }
+ },
+ "@vue/reactivity": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.27.tgz",
+ "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
+ "requires": {
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "@vue/runtime-core": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+ "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
+ "requires": {
+ "@vue/reactivity": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "@vue/runtime-dom": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+ "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
+ "requires": {
+ "@vue/runtime-core": "3.4.27",
+ "@vue/shared": "3.4.27",
+ "csstype": "^3.1.3"
+ },
+ "dependencies": {
+ "csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ }
+ }
+ },
+ "@vue/server-renderer": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+ "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
+ "requires": {
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "@vue/shared": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
+ },
+ "acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "async-validator": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+ "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "requires": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "bowser": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmmirror.com/bowser/-/bowser-2.11.0.tgz",
+ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.1.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.22.2",
+ "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.2.tgz",
+ "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001565",
+ "electron-to-chromium": "^1.4.601",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ }
+ },
+ "bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "requires": {
+ "run-applescript": "^7.0.0"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001570",
+ "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz",
+ "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chart.js": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.4.2.tgz",
+ "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==",
+ "requires": {
+ "@kurkle/color": "^0.3.0"
+ }
+ },
+ "chartjs-adapter-luxon": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmmirror.com/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.3.1.tgz",
+ "integrity": "sha512-yxHov3X8y+reIibl1o+j18xzrcdddCLqsXhriV2+aQ4hCR66IYFchlRXUvrJVoxglJ380pgytU7YWtoqdIgqhg==",
+ "requires": {}
+ },
+ "chartjs-adapter-moment": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
+ "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
+ "requires": {}
+ },
+ "chartjs-plugin-datalabels": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+ "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+ "requires": {}
+ },
+ "chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "css-render": {
+ "version": "0.15.12",
+ "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
+ "integrity": "sha512-eWzS66patiGkTTik+ipO9qNGZ+uNuGyTmnz6/+EJIiFg8+3yZRpnMwgFo8YdXhQRsiePzehnusrxVvugNjXzbw==",
+ "dev": true,
+ "requires": {
+ "@emotion/hash": "~0.8.0",
+ "csstype": "~3.0.5"
+ }
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "csstype": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
+ "dev": true
+ },
+ "date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.21.0"
+ }
+ },
+ "date-fns-tz": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/date-fns-tz/-/date-fns-tz-2.0.0.tgz",
+ "integrity": "sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "default-browser": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
+ "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
+ "dev": true,
+ "requires": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ }
+ },
+ "default-browser-id": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz",
+ "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
+ "dev": true
+ },
+ "define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "echarts": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz",
+ "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
+ "requires": {
+ "tslib": "2.3.0",
+ "zrender": "5.5.0"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.614",
+ "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz",
+ "integrity": "sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==",
+ "dev": true
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+ },
+ "error-stack-parser-es": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-0.1.1.tgz",
+ "integrity": "sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA==",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.20.2.tgz",
+ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+ "dev": true,
+ "requires": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true
+ },
+ "eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-plugin-prettier": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+ "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.6"
+ }
+ },
+ "eslint-plugin-vue": {
+ "version": "9.24.0",
+ "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz",
+ "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "globals": "^13.24.0",
+ "natural-compare": "^1.4.0",
+ "nth-check": "^2.1.1",
+ "postcss-selector-parser": "^6.0.15",
+ "semver": "^7.6.0",
+ "vue-eslint-parser": "^9.4.2",
+ "xml-name-validator": "^4.0.0"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ },
+ "espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ }
+ },
+ "esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "evtd": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/evtd/-/evtd-0.2.4.tgz",
+ "integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ }
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+ },
+ "fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.9.tgz",
+ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
+ },
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "hasown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz",
+ "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
+ "highlight.js": {
+ "version": "11.9.0",
+ "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz",
+ "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw=="
+ },
+ "html-tags": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.3.1.tgz",
+ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
+ "dev": true
+ },
+ "html-to-image": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz",
+ "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
+ },
+ "ignore": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.0.tgz",
+ "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+ "dev": true
+ },
+ "immutable": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.4.tgz",
+ "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dev": true,
+ "requires": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^3.0.0"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
+ "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+ "dev": true,
+ "requires": {
+ "is-inside-container": "^1.0.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true
+ },
+ "jsonc-parser": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "katex": {
+ "version": "0.16.10",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
+ "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
+ "requires": {
+ "commander": "^8.3.0"
+ }
+ },
+ "keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "local-pkg": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.3.tgz",
+ "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "luxon": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz",
+ "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA=="
+ },
+ "magic-string": {
+ "version": "0.30.10",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz",
+ "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
+ "requires": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ }
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mlly": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.6.1.tgz",
+ "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.11.3",
+ "pathe": "^1.1.2",
+ "pkg-types": "^1.0.3",
+ "ufo": "^1.3.2"
+ }
+ },
+ "moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
+ },
+ "mrmime": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
+ "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "naive-ui": {
+ "version": "2.38.1",
+ "resolved": "https://registry.npmjs.org/naive-ui/-/naive-ui-2.38.1.tgz",
+ "integrity": "sha512-AnU1FQ7K/CbhguAX++V4kCFjk7h7RvWt4nvZPRjORMpq+fUIlzD+EcQ5Cv1VqDloNF8+eMv4Akc2Ogacc9S+5A==",
+ "dev": true,
+ "requires": {
+ "@css-render/plugin-bem": "^0.15.12",
+ "@css-render/vue3-ssr": "^0.15.12",
+ "@types/katex": "^0.16.2",
+ "@types/lodash": "^4.14.198",
+ "@types/lodash-es": "^4.17.9",
+ "async-validator": "^4.2.5",
+ "css-render": "^0.15.12",
+ "csstype": "^3.1.3",
+ "date-fns": "^2.30.0",
+ "date-fns-tz": "^2.0.0",
+ "evtd": "^0.2.4",
+ "highlight.js": "^11.8.0",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "seemly": "^0.3.8",
+ "treemate": "^0.3.11",
+ "vdirs": "^0.1.8",
+ "vooks": "^0.2.12",
+ "vueuc": "^0.4.58"
+ },
+ "dependencies": {
+ "csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true
+ }
+ }
+ },
+ "nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "open": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz",
+ "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==",
+ "dev": true,
+ "requires": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "is-wsl": "^3.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "requires": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ }
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true
+ },
+ "perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pinia": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
+ "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+ "requires": {
+ "@vue/devtools-api": "^6.5.0",
+ "vue-demi": ">=0.14.5"
+ },
+ "dependencies": {
+ "vue-demi": {
+ "version": "0.14.6",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz",
+ "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+ "requires": {}
+ }
+ }
+ },
+ "pinyin-pro": {
+ "version": "3.19.6",
+ "resolved": "https://registry.npmmirror.com/pinyin-pro/-/pinyin-pro-3.19.6.tgz",
+ "integrity": "sha512-oWb34orr12+DjXf6gtGMB+gIpjRi7DZzyJE66ultbmNzVhpimM/utGtMI8GcbOy/lb26Ph/nogwNYriRPu+SGQ=="
+ },
+ "pkg-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz",
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "dev": true,
+ "requires": {
+ "jsonc-parser": "^3.2.0",
+ "mlly": "^1.2.0",
+ "pathe": "^1.1.0"
+ }
+ },
+ "postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "requires": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "reconnecting-websocket": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz",
+ "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng=="
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "dev": true
+ },
+ "resize-detector": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
+ "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ=="
+ },
+ "resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rollup": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.14.0.tgz",
+ "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
+ "dev": true,
+ "requires": {
+ "@rollup/rollup-android-arm-eabi": "4.14.0",
+ "@rollup/rollup-android-arm64": "4.14.0",
+ "@rollup/rollup-darwin-arm64": "4.14.0",
+ "@rollup/rollup-darwin-x64": "4.14.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.14.0",
+ "@rollup/rollup-linux-arm64-musl": "4.14.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.14.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.14.0",
+ "@rollup/rollup-linux-x64-gnu": "4.14.0",
+ "@rollup/rollup-linux-x64-musl": "4.14.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.14.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.14.0",
+ "@rollup/rollup-win32-x64-msvc": "4.14.0",
+ "@types/estree": "1.0.5",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "run-applescript": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
+ "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
+ "dev": true
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "sass": {
+ "version": "1.74.1",
+ "resolved": "https://registry.npmmirror.com/sass/-/sass-1.74.1.tgz",
+ "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "scule": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
+ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
+ "dev": true
+ },
+ "seemly": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/seemly/-/seemly-0.3.8.tgz",
+ "integrity": "sha512-MW8Qs6vbzo0pHmDpFSYPna+lwpZ6Zk1ancbajw/7E8TKtHdV+1DfZZD+kKJEhG/cAoB/i+LiT+5msZOqj0DwRA==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "sirv": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+ "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
+ "dev": true,
+ "requires": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ }
+ },
+ "source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg=="
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "strip-literal": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-1.3.0.tgz",
+ "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.10.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "svg-tags": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz",
+ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+ "dev": true
+ },
+ "synckit": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.8.8.tgz",
+ "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
+ "dev": true,
+ "requires": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true
+ },
+ "treemate": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmmirror.com/treemate/-/treemate-0.3.11.tgz",
+ "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.4.4.tgz",
+ "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
+ "devOptional": true
+ },
+ "ufo": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.5.3.tgz",
+ "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
+ "dev": true
+ },
+ "unimport": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmmirror.com/unimport/-/unimport-3.7.1.tgz",
+ "integrity": "sha512-V9HpXYfsZye5bPPYUgs0Otn3ODS1mDUciaBlXljI4C2fTwfFpvFZRywmlOu943puN9sncxROMZhsZCjNXEpzEQ==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.1.0",
+ "acorn": "^8.11.2",
+ "escape-string-regexp": "^5.0.0",
+ "estree-walker": "^3.0.3",
+ "fast-glob": "^3.3.2",
+ "local-pkg": "^0.5.0",
+ "magic-string": "^0.30.5",
+ "mlly": "^1.4.2",
+ "pathe": "^1.1.1",
+ "pkg-types": "^1.0.3",
+ "scule": "^1.1.1",
+ "strip-literal": "^1.3.0",
+ "unplugin": "^1.5.1"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "local-pkg": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz",
+ "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+ "dev": true,
+ "requires": {
+ "mlly": "^1.4.2",
+ "pkg-types": "^1.0.3"
+ }
+ }
+ }
+ },
+ "universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true
+ },
+ "unplugin": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.10.1.tgz",
+ "integrity": "sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.11.3",
+ "chokidar": "^3.6.0",
+ "webpack-sources": "^3.2.3",
+ "webpack-virtual-modules": "^0.6.1"
+ }
+ },
+ "unplugin-auto-import": {
+ "version": "0.17.5",
+ "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.17.5.tgz",
+ "integrity": "sha512-fHNDkDSxv3PGagX1wmKBYBkgaM4AKAgZmdJw/bxjhNljx9KSXSgHpGfX0MwUrq9qw6q1bhHIZVWyOwoY2koo4w==",
+ "dev": true,
+ "requires": {
+ "@antfu/utils": "^0.7.7",
+ "@rollup/pluginutils": "^5.1.0",
+ "fast-glob": "^3.3.2",
+ "local-pkg": "^0.5.0",
+ "magic-string": "^0.30.5",
+ "minimatch": "^9.0.3",
+ "unimport": "^3.7.1",
+ "unplugin": "^1.6.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "local-pkg": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz",
+ "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+ "dev": true,
+ "requires": {
+ "mlly": "^1.4.2",
+ "pkg-types": "^1.0.3"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "unplugin-vue-components": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.26.0.tgz",
+ "integrity": "sha512-s7IdPDlnOvPamjunVxw8kNgKNK8A5KM1YpK5j/p97jEKTjlPNrA0nZBiSfAKKlK1gWZuyWXlKL5dk3EDw874LQ==",
+ "dev": true,
+ "requires": {
+ "@antfu/utils": "^0.7.6",
+ "@rollup/pluginutils": "^5.0.4",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.1",
+ "local-pkg": "^0.4.3",
+ "magic-string": "^0.30.3",
+ "minimatch": "^9.0.3",
+ "resolve": "^1.22.4",
+ "unplugin": "^1.4.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "vdirs": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/vdirs/-/vdirs-0.1.8.tgz",
+ "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
+ "dev": true,
+ "requires": {
+ "evtd": "^0.2.2"
+ }
+ },
+ "vite": {
+ "version": "5.2.8",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-5.2.8.tgz",
+ "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.20.1",
+ "fsevents": "~2.3.3",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ }
+ },
+ "vite-plugin-inspect": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-0.8.3.tgz",
+ "integrity": "sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og==",
+ "dev": true,
+ "requires": {
+ "@antfu/utils": "^0.7.7",
+ "@rollup/pluginutils": "^5.1.0",
+ "debug": "^4.3.4",
+ "error-stack-parser-es": "^0.1.1",
+ "fs-extra": "^11.2.0",
+ "open": "^10.0.3",
+ "perfect-debounce": "^1.0.0",
+ "picocolors": "^1.0.0",
+ "sirv": "^2.0.4"
+ }
+ },
+ "vooks": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/vooks/-/vooks-0.2.12.tgz",
+ "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
+ "dev": true,
+ "requires": {
+ "evtd": "^0.2.2"
+ }
+ },
+ "vue": {
+ "version": "3.4.27",
+ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.27.tgz",
+ "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
+ "requires": {
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-sfc": "3.4.27",
+ "@vue/runtime-dom": "3.4.27",
+ "@vue/server-renderer": "3.4.27",
+ "@vue/shared": "3.4.27"
+ }
+ },
+ "vue-axios": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmmirror.com/vue-axios/-/vue-axios-3.5.2.tgz",
+ "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==",
+ "requires": {}
+ },
+ "vue-chartjs": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmmirror.com/vue-chartjs/-/vue-chartjs-5.3.0.tgz",
+ "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==",
+ "requires": {}
+ },
+ "vue-echarts": {
+ "version": "6.6.9",
+ "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.6.9.tgz",
+ "integrity": "sha512-mojIq3ZvsjabeVmDthhAUDV8Kgf2Rr/X4lV4da7gEFd1fP05gcSJ0j7wa7HQkW5LlFmF2gdCJ8p4Chas6NNIQQ==",
+ "requires": {
+ "resize-detector": "^0.3.0",
+ "vue-demi": "^0.13.11"
+ },
+ "dependencies": {
+ "vue-demi": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
+ "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+ "requires": {}
+ }
+ }
+ },
+ "vue-eslint-parser": {
+ "version": "9.4.2",
+ "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz",
+ "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "eslint-scope": "^7.1.1",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.3.1",
+ "esquery": "^1.4.0",
+ "lodash": "^4.17.21",
+ "semver": "^7.3.6"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "vue-router": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.3.0.tgz",
+ "integrity": "sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==",
+ "requires": {
+ "@vue/devtools-api": "^6.5.1"
+ }
+ },
+ "vue-slicksort": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/vue-slicksort/-/vue-slicksort-2.0.5.tgz",
+ "integrity": "sha512-fXz1YrNjhUbJK7o0tMk27mIr4pMAZYLSYvtmLazCtfpvz+zafPCn34ILDL8B7hT7WLVZKreYs6JVe5VWymqmzA==",
+ "requires": {}
+ },
+ "vueuc": {
+ "version": "0.4.58",
+ "resolved": "https://registry.npmjs.org/vueuc/-/vueuc-0.4.58.tgz",
+ "integrity": "sha512-Wnj/N8WbPRSxSt+9ji1jtDHPzda5h2OH/0sFBhvdxDRuyCZbjGg3/cKMaKqEoe+dErTexG2R+i6Q8S/Toq1MYg==",
+ "dev": true,
+ "requires": {
+ "@css-render/vue3-ssr": "^0.15.10",
+ "@juggle/resize-observer": "^3.3.1",
+ "css-render": "^0.15.10",
+ "evtd": "^0.2.4",
+ "seemly": "^0.3.6",
+ "vdirs": "^0.1.4",
+ "vooks": "^0.2.4"
+ }
+ },
+ "webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "dev": true
+ },
+ "webpack-virtual-modules": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz",
+ "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "xml-name-validator": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
+ },
+ "zrender": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz",
+ "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
+ "requires": {
+ "tslib": "2.3.0"
+ }
+ }
+ }
+}
diff --git a/ui/package.json b/ui/package.json
new file mode 100644
index 000000000..23c08bcef
--- /dev/null
+++ b/ui/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "mower-ui",
+ "type": "module",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "vite --profile --open",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
+ "format": "prettier --write src/"
+ },
+ "dependencies": {
+ "@vicons/fluent": "^0.12.0",
+ "@vicons/ionicons4": "^0.12.0",
+ "@vicons/ionicons5": "^0.12.0",
+ "@vicons/material": "^0.12.0",
+ "@vicons/tabler": "^0.12.0",
+ "axios": "^1.6.8",
+ "bowser": "^2.11.0",
+ "chart.js": "^4.4.2",
+ "chartjs-adapter-luxon": "^1.3.1",
+ "chartjs-adapter-moment": "^1.0.1",
+ "chartjs-plugin-datalabels": "^2.2.0",
+ "echarts": "^5.5.0",
+ "file-saver": "^2.0.5",
+ "highlight.js": "^11.9.0",
+ "html-to-image": "^1.11.11",
+ "katex": "^0.16.10",
+ "luxon": "^3.4.4",
+ "moment": "^2.30.1",
+ "pinia": "^2.1.7",
+ "pinyin-pro": "^3.19.6",
+ "prettier": "^3.3.3",
+ "reconnecting-websocket": "^4.4.0",
+ "vue": "^3.4.27",
+ "vue-axios": "^3.5.2",
+ "vue-chartjs": "^5.3.0",
+ "vue-echarts": "^6.6.9",
+ "vue-router": "^4.3.0",
+ "vue-slicksort": "^2.0.5"
+ },
+ "devDependencies": {
+ "@rushstack/eslint-patch": "^1.10.1",
+ "@vicons/carbon": "^0.12.0",
+ "@vicons/fa": "^0.12.0",
+ "@vitejs/plugin-vue": "^5.0.4",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "@vue/eslint-config-prettier": "^9.0.0",
+ "eslint": "^8.57.0",
+ "eslint-plugin-vue": "^9.24.0",
+ "naive-ui": "^2.38.1",
+ "sass": "^1.74.1",
+ "typescript": "^5.4.4",
+ "unplugin-auto-import": "^0.17.5",
+ "unplugin-vue-components": "^0.26.0",
+ "vite": "^5.2.8",
+ "vite-plugin-inspect": "^0.8.3"
+ }
+}
diff --git a/ui/public/avatar/.webp b/ui/public/avatar/.webp
new file mode 100644
index 000000000..d2e8417ce
Binary files /dev/null and b/ui/public/avatar/.webp differ
diff --git a/ui/public/avatar/12F.webp b/ui/public/avatar/12F.webp
new file mode 100644
index 000000000..e986c7088
Binary files /dev/null and b/ui/public/avatar/12F.webp differ
diff --git a/ui/public/avatar/Castle-3.webp b/ui/public/avatar/Castle-3.webp
new file mode 100644
index 000000000..61b9b9d13
Binary files /dev/null and b/ui/public/avatar/Castle-3.webp differ
diff --git a/ui/public/avatar/Current.webp b/ui/public/avatar/Current.webp
new file mode 100644
index 000000000..591ebb5c0
Binary files /dev/null and b/ui/public/avatar/Current.webp differ
diff --git a/ui/public/avatar/Free.webp b/ui/public/avatar/Free.webp
new file mode 100644
index 000000000..f29fce9c9
Binary files /dev/null and b/ui/public/avatar/Free.webp differ
diff --git a/ui/public/avatar/Friston-3.webp b/ui/public/avatar/Friston-3.webp
new file mode 100644
index 000000000..097125280
Binary files /dev/null and b/ui/public/avatar/Friston-3.webp differ
diff --git a/ui/public/avatar/Lancet-2.webp b/ui/public/avatar/Lancet-2.webp
new file mode 100644
index 000000000..b7cfbaf8a
Binary files /dev/null and b/ui/public/avatar/Lancet-2.webp differ
diff --git a/ui/public/avatar/PhonoR-0.webp b/ui/public/avatar/PhonoR-0.webp
new file mode 100644
index 000000000..80d7f9d10
Binary files /dev/null and b/ui/public/avatar/PhonoR-0.webp differ
diff --git a/ui/public/avatar/THRM-EX.webp b/ui/public/avatar/THRM-EX.webp
new file mode 100644
index 000000000..da6bd336f
Binary files /dev/null and b/ui/public/avatar/THRM-EX.webp differ
diff --git a/ui/public/avatar/U-Official.webp b/ui/public/avatar/U-Official.webp
new file mode 100644
index 000000000..91256b563
Binary files /dev/null and b/ui/public/avatar/U-Official.webp differ
diff --git a/ui/public/avatar/W.webp b/ui/public/avatar/W.webp
new file mode 100644
index 000000000..853387ab4
Binary files /dev/null and b/ui/public/avatar/W.webp differ
diff --git "a/ui/public/avatar/\344\270\207\351\241\267.webp" "b/ui/public/avatar/\344\270\207\351\241\267.webp"
new file mode 100644
index 000000000..7654e08f7
Binary files /dev/null and "b/ui/public/avatar/\344\270\207\351\241\267.webp" differ
diff --git "a/ui/public/avatar/\344\270\264\345\205\211.webp" "b/ui/public/avatar/\344\270\264\345\205\211.webp"
new file mode 100644
index 000000000..07af61084
Binary files /dev/null and "b/ui/public/avatar/\344\270\264\345\205\211.webp" differ
diff --git "a/ui/public/avatar/\344\271\214\345\260\224\346\257\224\345\256\211.webp" "b/ui/public/avatar/\344\271\214\345\260\224\346\257\224\345\256\211.webp"
new file mode 100644
index 000000000..d92315c3c
Binary files /dev/null and "b/ui/public/avatar/\344\271\214\345\260\224\346\257\224\345\256\211.webp" differ
diff --git "a/ui/public/avatar/\344\271\214\346\234\211.webp" "b/ui/public/avatar/\344\271\214\346\234\211.webp"
new file mode 100644
index 000000000..3ef46fe9d
Binary files /dev/null and "b/ui/public/avatar/\344\271\214\346\234\211.webp" differ
diff --git "a/ui/public/avatar/\344\271\235\350\211\262\351\271\277.webp" "b/ui/public/avatar/\344\271\235\350\211\262\351\271\277.webp"
new file mode 100644
index 000000000..cbffca93c
Binary files /dev/null and "b/ui/public/avatar/\344\271\235\350\211\262\351\271\277.webp" differ
diff --git "a/ui/public/avatar/\344\272\232\345\217\266.webp" "b/ui/public/avatar/\344\272\232\345\217\266.webp"
new file mode 100644
index 000000000..2e3332702
Binary files /dev/null and "b/ui/public/avatar/\344\272\232\345\217\266.webp" differ
diff --git "a/ui/public/avatar/\344\273\207\347\231\275.webp" "b/ui/public/avatar/\344\273\207\347\231\275.webp"
new file mode 100644
index 000000000..008a7c8df
Binary files /dev/null and "b/ui/public/avatar/\344\273\207\347\231\275.webp" differ
diff --git "a/ui/public/avatar/\344\273\244.webp" "b/ui/public/avatar/\344\273\244.webp"
new file mode 100644
index 000000000..967912462
Binary files /dev/null and "b/ui/public/avatar/\344\273\244.webp" differ
diff --git "a/ui/public/avatar/\344\274\212\345\206\205\344\270\235.webp" "b/ui/public/avatar/\344\274\212\345\206\205\344\270\235.webp"
new file mode 100644
index 000000000..c8a2ed97c
Binary files /dev/null and "b/ui/public/avatar/\344\274\212\345\206\205\344\270\235.webp" differ
diff --git "a/ui/public/avatar/\344\274\212\346\241\221.webp" "b/ui/public/avatar/\344\274\212\346\241\221.webp"
new file mode 100644
index 000000000..cdbc1677d
Binary files /dev/null and "b/ui/public/avatar/\344\274\212\346\241\221.webp" differ
diff --git "a/ui/public/avatar/\344\274\212\350\212\231\345\210\251\347\211\271.webp" "b/ui/public/avatar/\344\274\212\350\212\231\345\210\251\347\211\271.webp"
new file mode 100644
index 000000000..b70cfcde0
Binary files /dev/null and "b/ui/public/avatar/\344\274\212\350\212\231\345\210\251\347\211\271.webp" differ
diff --git "a/ui/public/avatar/\344\274\221\350\260\237\346\226\257.webp" "b/ui/public/avatar/\344\274\221\350\260\237\346\226\257.webp"
new file mode 100644
index 000000000..02ef2c292
Binary files /dev/null and "b/ui/public/avatar/\344\274\221\350\260\237\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\344\274\272\345\244\234.webp" "b/ui/public/avatar/\344\274\272\345\244\234.webp"
new file mode 100644
index 000000000..f726ab894
Binary files /dev/null and "b/ui/public/avatar/\344\274\272\345\244\234.webp" differ
diff --git "a/ui/public/avatar/\344\275\206\344\271\246.webp" "b/ui/public/avatar/\344\275\206\344\271\246.webp"
new file mode 100644
index 000000000..84425097a
Binary files /dev/null and "b/ui/public/avatar/\344\275\206\344\271\246.webp" differ
diff --git "a/ui/public/avatar/\344\275\251\344\275\251.webp" "b/ui/public/avatar/\344\275\251\344\275\251.webp"
new file mode 100644
index 000000000..7c09669aa
Binary files /dev/null and "b/ui/public/avatar/\344\275\251\344\275\251.webp" differ
diff --git "a/ui/public/avatar/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210.webp" "b/ui/public/avatar/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210.webp"
new file mode 100644
index 000000000..92075766a
Binary files /dev/null and "b/ui/public/avatar/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210.webp" differ
diff --git "a/ui/public/avatar/\345\202\200\345\275\261.webp" "b/ui/public/avatar/\345\202\200\345\275\261.webp"
new file mode 100644
index 000000000..dfc6dfc06
Binary files /dev/null and "b/ui/public/avatar/\345\202\200\345\275\261.webp" differ
diff --git "a/ui/public/avatar/\345\205\213\346\264\233\344\270\235.webp" "b/ui/public/avatar/\345\205\213\346\264\233\344\270\235.webp"
new file mode 100644
index 000000000..fad10275f
Binary files /dev/null and "b/ui/public/avatar/\345\205\213\346\264\233\344\270\235.webp" differ
diff --git "a/ui/public/avatar/\345\206\260\351\205\277.webp" "b/ui/public/avatar/\345\206\260\351\205\277.webp"
new file mode 100644
index 000000000..cb27de2fd
Binary files /dev/null and "b/ui/public/avatar/\345\206\260\351\205\277.webp" differ
diff --git "a/ui/public/avatar/\345\207\233\345\206\254.webp" "b/ui/public/avatar/\345\207\233\345\206\254.webp"
new file mode 100644
index 000000000..45f0fef4a
Binary files /dev/null and "b/ui/public/avatar/\345\207\233\345\206\254.webp" differ
diff --git "a/ui/public/avatar/\345\207\233\350\247\206.webp" "b/ui/public/avatar/\345\207\233\350\247\206.webp"
new file mode 100644
index 000000000..5e646cb78
Binary files /dev/null and "b/ui/public/avatar/\345\207\233\350\247\206.webp" differ
diff --git "a/ui/public/avatar/\345\207\257\345\260\224\345\270\214.webp" "b/ui/public/avatar/\345\207\257\345\260\224\345\270\214.webp"
new file mode 100644
index 000000000..316cf346d
Binary files /dev/null and "b/ui/public/avatar/\345\207\257\345\260\224\345\270\214.webp" differ
diff --git "a/ui/public/avatar/\345\210\235\351\233\252.webp" "b/ui/public/avatar/\345\210\235\351\233\252.webp"
new file mode 100644
index 000000000..9a38d48d4
Binary files /dev/null and "b/ui/public/avatar/\345\210\235\351\233\252.webp" differ
diff --git "a/ui/public/avatar/\345\210\272\347\216\253.webp" "b/ui/public/avatar/\345\210\272\347\216\253.webp"
new file mode 100644
index 000000000..42c3f3b47
Binary files /dev/null and "b/ui/public/avatar/\345\210\272\347\216\253.webp" differ
diff --git "a/ui/public/avatar/\345\210\273\344\277\204\346\237\217.webp" "b/ui/public/avatar/\345\210\273\344\277\204\346\237\217.webp"
new file mode 100644
index 000000000..eaf359f97
Binary files /dev/null and "b/ui/public/avatar/\345\210\273\344\277\204\346\237\217.webp" differ
diff --git "a/ui/public/avatar/\345\210\273\345\210\200.webp" "b/ui/public/avatar/\345\210\273\345\210\200.webp"
new file mode 100644
index 000000000..d7cf6f2c7
Binary files /dev/null and "b/ui/public/avatar/\345\210\273\345\210\200.webp" differ
diff --git "a/ui/public/avatar/\345\214\273\347\224\237.webp" "b/ui/public/avatar/\345\214\273\347\224\237.webp"
new file mode 100644
index 000000000..b6f2274cb
Binary files /dev/null and "b/ui/public/avatar/\345\214\273\347\224\237.webp" differ
diff --git "a/ui/public/avatar/\345\215\216\346\263\225\347\220\263.webp" "b/ui/public/avatar/\345\215\216\346\263\225\347\220\263.webp"
new file mode 100644
index 000000000..f6fb95af7
Binary files /dev/null and "b/ui/public/avatar/\345\215\216\346\263\225\347\220\263.webp" differ
diff --git "a/ui/public/avatar/\345\215\241\345\244\253\345\215\241.webp" "b/ui/public/avatar/\345\215\241\345\244\253\345\215\241.webp"
new file mode 100644
index 000000000..64192e697
Binary files /dev/null and "b/ui/public/avatar/\345\215\241\345\244\253\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\345\215\241\346\266\205\345\210\251\345\256\211.webp" "b/ui/public/avatar/\345\215\241\346\266\205\345\210\251\345\256\211.webp"
new file mode 100644
index 000000000..ada0479c7
Binary files /dev/null and "b/ui/public/avatar/\345\215\241\346\266\205\345\210\251\345\256\211.webp" differ
diff --git "a/ui/public/avatar/\345\215\241\347\274\207.webp" "b/ui/public/avatar/\345\215\241\347\274\207.webp"
new file mode 100644
index 000000000..5d271215f
Binary files /dev/null and "b/ui/public/avatar/\345\215\241\347\274\207.webp" differ
diff --git "a/ui/public/avatar/\345\215\241\350\276\276.webp" "b/ui/public/avatar/\345\215\241\350\276\276.webp"
new file mode 100644
index 000000000..c98205805
Binary files /dev/null and "b/ui/public/avatar/\345\215\241\350\276\276.webp" differ
diff --git "a/ui/public/avatar/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254.webp" "b/ui/public/avatar/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254.webp"
new file mode 100644
index 000000000..ebabe8baa
Binary files /dev/null and "b/ui/public/avatar/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254.webp" differ
diff --git "a/ui/public/avatar/\345\217\214\346\234\210.webp" "b/ui/public/avatar/\345\217\214\346\234\210.webp"
new file mode 100644
index 000000000..06f8930ab
Binary files /dev/null and "b/ui/public/avatar/\345\217\214\346\234\210.webp" differ
diff --git "a/ui/public/avatar/\345\217\244\347\261\263.webp" "b/ui/public/avatar/\345\217\244\347\261\263.webp"
new file mode 100644
index 000000000..eea7db24b
Binary files /dev/null and "b/ui/public/avatar/\345\217\244\347\261\263.webp" differ
diff --git "a/ui/public/avatar/\345\217\257\351\242\202.webp" "b/ui/public/avatar/\345\217\257\351\242\202.webp"
new file mode 100644
index 000000000..e820a2c96
Binary files /dev/null and "b/ui/public/avatar/\345\217\257\351\242\202.webp" differ
diff --git "a/ui/public/avatar/\345\217\262\345\260\224\347\211\271\345\260\224.webp" "b/ui/public/avatar/\345\217\262\345\260\224\347\211\271\345\260\224.webp"
new file mode 100644
index 000000000..a25539143
Binary files /dev/null and "b/ui/public/avatar/\345\217\262\345\260\224\347\211\271\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\345\217\262\351\203\275\345\215\216\345\276\267.webp" "b/ui/public/avatar/\345\217\262\351\203\275\345\215\216\345\276\267.webp"
new file mode 100644
index 000000000..7c78358dd
Binary files /dev/null and "b/ui/public/avatar/\345\217\262\351\203\275\345\215\216\345\276\267.webp" differ
diff --git "a/ui/public/avatar/\345\217\267\350\247\222.webp" "b/ui/public/avatar/\345\217\267\350\247\222.webp"
new file mode 100644
index 000000000..0df2140b5
Binary files /dev/null and "b/ui/public/avatar/\345\217\267\350\247\222.webp" differ
diff --git "a/ui/public/avatar/\345\220\275.webp" "b/ui/public/avatar/\345\220\275.webp"
new file mode 100644
index 000000000..f52d33ac6
Binary files /dev/null and "b/ui/public/avatar/\345\220\275.webp" differ
diff --git "a/ui/public/avatar/\345\222\214\345\274\246.webp" "b/ui/public/avatar/\345\222\214\345\274\246.webp"
new file mode 100644
index 000000000..9127b4a0c
Binary files /dev/null and "b/ui/public/avatar/\345\222\214\345\274\246.webp" differ
diff --git "a/ui/public/avatar/\345\223\210\346\264\233\345\276\267.webp" "b/ui/public/avatar/\345\223\210\346\264\233\345\276\267.webp"
new file mode 100644
index 000000000..d5fa8ed7f
Binary files /dev/null and "b/ui/public/avatar/\345\223\210\346\264\233\345\276\267.webp" differ
diff --git "a/ui/public/avatar/\345\230\211\347\273\264\345\260\224.webp" "b/ui/public/avatar/\345\230\211\347\273\264\345\260\224.webp"
new file mode 100644
index 000000000..e826ca574
Binary files /dev/null and "b/ui/public/avatar/\345\230\211\347\273\264\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\345\233\233\346\234\210.webp" "b/ui/public/avatar/\345\233\233\346\234\210.webp"
new file mode 100644
index 000000000..283ea0fdf
Binary files /dev/null and "b/ui/public/avatar/\345\233\233\346\234\210.webp" differ
diff --git "a/ui/public/avatar/\345\233\240\351\231\200\347\275\227.webp" "b/ui/public/avatar/\345\233\240\351\231\200\347\275\227.webp"
new file mode 100644
index 000000000..69ce754ed
Binary files /dev/null and "b/ui/public/avatar/\345\233\240\351\231\200\347\275\227.webp" differ
diff --git "a/ui/public/avatar/\345\233\276\350\200\266.webp" "b/ui/public/avatar/\345\233\276\350\200\266.webp"
new file mode 100644
index 000000000..eab968fde
Binary files /dev/null and "b/ui/public/avatar/\345\233\276\350\200\266.webp" differ
diff --git "a/ui/public/avatar/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272.webp" "b/ui/public/avatar/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272.webp"
new file mode 100644
index 000000000..5fb0b7d2b
Binary files /dev/null and "b/ui/public/avatar/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272.webp" differ
diff --git "a/ui/public/avatar/\345\234\260\347\201\265.webp" "b/ui/public/avatar/\345\234\260\347\201\265.webp"
new file mode 100644
index 000000000..12f7044d5
Binary files /dev/null and "b/ui/public/avatar/\345\234\260\347\201\265.webp" differ
diff --git "a/ui/public/avatar/\345\235\232\351\233\267.webp" "b/ui/public/avatar/\345\235\232\351\233\267.webp"
new file mode 100644
index 000000000..6bffb1702
Binary files /dev/null and "b/ui/public/avatar/\345\235\232\351\233\267.webp" differ
diff --git "a/ui/public/avatar/\345\237\203\346\213\211\346\211\230.webp" "b/ui/public/avatar/\345\237\203\346\213\211\346\211\230.webp"
new file mode 100644
index 000000000..5c48960cb
Binary files /dev/null and "b/ui/public/avatar/\345\237\203\346\213\211\346\211\230.webp" differ
diff --git "a/ui/public/avatar/\345\241\221\345\277\203.webp" "b/ui/public/avatar/\345\241\221\345\277\203.webp"
new file mode 100644
index 000000000..0335faa7a
Binary files /dev/null and "b/ui/public/avatar/\345\241\221\345\277\203.webp" differ
diff --git "a/ui/public/avatar/\345\241\236\351\233\267\345\250\205.webp" "b/ui/public/avatar/\345\241\236\351\233\267\345\250\205.webp"
new file mode 100644
index 000000000..1f1e4f7db
Binary files /dev/null and "b/ui/public/avatar/\345\241\236\351\233\267\345\250\205.webp" differ
diff --git "a/ui/public/avatar/\345\244\217\346\240\216.webp" "b/ui/public/avatar/\345\244\217\346\240\216.webp"
new file mode 100644
index 000000000..8cab018f6
Binary files /dev/null and "b/ui/public/avatar/\345\244\217\346\240\216.webp" differ
diff --git "a/ui/public/avatar/\345\244\225.webp" "b/ui/public/avatar/\345\244\225.webp"
new file mode 100644
index 000000000..3c65457c8
Binary files /dev/null and "b/ui/public/avatar/\345\244\225.webp" differ
diff --git "a/ui/public/avatar/\345\244\232\350\220\235\350\245\277.webp" "b/ui/public/avatar/\345\244\232\350\220\235\350\245\277.webp"
new file mode 100644
index 000000000..f5c9ddca8
Binary files /dev/null and "b/ui/public/avatar/\345\244\232\350\220\235\350\245\277.webp" differ
diff --git "a/ui/public/avatar/\345\244\234\345\210\200.webp" "b/ui/public/avatar/\345\244\234\345\210\200.webp"
new file mode 100644
index 000000000..e2c1465be
Binary files /dev/null and "b/ui/public/avatar/\345\244\234\345\210\200.webp" differ
diff --git "a/ui/public/avatar/\345\244\234\345\215\212.webp" "b/ui/public/avatar/\345\244\234\345\215\212.webp"
new file mode 100644
index 000000000..38f733455
Binary files /dev/null and "b/ui/public/avatar/\345\244\234\345\215\212.webp" differ
diff --git "a/ui/public/avatar/\345\244\234\347\203\237.webp" "b/ui/public/avatar/\345\244\234\347\203\237.webp"
new file mode 100644
index 000000000..f58efc5ce
Binary files /dev/null and "b/ui/public/avatar/\345\244\234\347\203\237.webp" differ
diff --git "a/ui/public/avatar/\345\244\234\350\216\272.webp" "b/ui/public/avatar/\345\244\234\350\216\272.webp"
new file mode 100644
index 000000000..a3d0933bb
Binary files /dev/null and "b/ui/public/avatar/\345\244\234\350\216\272.webp" differ
diff --git "a/ui/public/avatar/\345\244\234\351\255\224.webp" "b/ui/public/avatar/\345\244\234\351\255\224.webp"
new file mode 100644
index 000000000..3cf72f5bf
Binary files /dev/null and "b/ui/public/avatar/\345\244\234\351\255\224.webp" differ
diff --git "a/ui/public/avatar/\345\244\251\347\201\253.webp" "b/ui/public/avatar/\345\244\251\347\201\253.webp"
new file mode 100644
index 000000000..eebe60254
Binary files /dev/null and "b/ui/public/avatar/\345\244\251\347\201\253.webp" differ
diff --git "a/ui/public/avatar/\345\245\245\346\226\257\345\241\224.webp" "b/ui/public/avatar/\345\245\245\346\226\257\345\241\224.webp"
new file mode 100644
index 000000000..8abccc4be
Binary files /dev/null and "b/ui/public/avatar/\345\245\245\346\226\257\345\241\224.webp" differ
diff --git "a/ui/public/avatar/\345\245\245\350\276\276.webp" "b/ui/public/avatar/\345\245\245\350\276\276.webp"
new file mode 100644
index 000000000..70d07599b
Binary files /dev/null and "b/ui/public/avatar/\345\245\245\350\276\276.webp" differ
diff --git "a/ui/public/avatar/\345\246\256\350\212\231.webp" "b/ui/public/avatar/\345\246\256\350\212\231.webp"
new file mode 100644
index 000000000..132334648
Binary files /dev/null and "b/ui/public/avatar/\345\246\256\350\212\231.webp" differ
diff --git "a/ui/public/avatar/\345\250\234\344\273\201\345\233\276\344\272\232.webp" "b/ui/public/avatar/\345\250\234\344\273\201\345\233\276\344\272\232.webp"
new file mode 100644
index 000000000..6ee95659b
Binary files /dev/null and "b/ui/public/avatar/\345\250\234\344\273\201\345\233\276\344\272\232.webp" differ
diff --git "a/ui/public/avatar/\345\255\220\346\234\210.webp" "b/ui/public/avatar/\345\255\220\346\234\210.webp"
new file mode 100644
index 000000000..a11939ce0
Binary files /dev/null and "b/ui/public/avatar/\345\255\220\346\234\210.webp" differ
diff --git "a/ui/public/avatar/\345\255\221.webp" "b/ui/public/avatar/\345\255\221.webp"
new file mode 100644
index 000000000..d305b69f4
Binary files /dev/null and "b/ui/public/avatar/\345\255\221.webp" differ
diff --git "a/ui/public/avatar/\345\256\210\346\236\227\344\272\272.webp" "b/ui/public/avatar/\345\256\210\346\236\227\344\272\272.webp"
new file mode 100644
index 000000000..4ed7eeeb0
Binary files /dev/null and "b/ui/public/avatar/\345\256\210\346\236\227\344\272\272.webp" differ
diff --git "a/ui/public/avatar/\345\256\211\345\223\262\346\213\211.webp" "b/ui/public/avatar/\345\256\211\345\223\262\346\213\211.webp"
new file mode 100644
index 000000000..6abb146ff
Binary files /dev/null and "b/ui/public/avatar/\345\256\211\345\223\262\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\345\256\211\345\276\267\345\210\207\345\260\224.webp" "b/ui/public/avatar/\345\256\211\345\276\267\345\210\207\345\260\224.webp"
new file mode 100644
index 000000000..435b02ba8
Binary files /dev/null and "b/ui/public/avatar/\345\256\211\345\276\267\345\210\207\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\345\256\211\346\257\224\345\260\224.webp" "b/ui/public/avatar/\345\256\211\346\257\224\345\260\224.webp"
new file mode 100644
index 000000000..307e30edf
Binary files /dev/null and "b/ui/public/avatar/\345\256\211\346\257\224\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\345\256\211\346\264\201\350\216\211\345\250\234.webp" "b/ui/public/avatar/\345\256\211\346\264\201\350\216\211\345\250\234.webp"
new file mode 100644
index 000000000..7eca751ac
Binary files /dev/null and "b/ui/public/avatar/\345\256\211\346\264\201\350\216\211\345\250\234.webp" differ
diff --git "a/ui/public/avatar/\345\256\211\350\265\233\345\260\224.webp" "b/ui/public/avatar/\345\256\211\350\265\233\345\260\224.webp"
new file mode 100644
index 000000000..c133c6dac
Binary files /dev/null and "b/ui/public/avatar/\345\256\211\350\265\233\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\345\256\264.webp" "b/ui/public/avatar/\345\256\264.webp"
new file mode 100644
index 000000000..5f3e8d0d1
Binary files /dev/null and "b/ui/public/avatar/\345\256\264.webp" differ
diff --git "a/ui/public/avatar/\345\257\222\346\252\200.webp" "b/ui/public/avatar/\345\257\222\346\252\200.webp"
new file mode 100644
index 000000000..75cbfcaf1
Binary files /dev/null and "b/ui/public/avatar/\345\257\222\346\252\200.webp" differ
diff --git "a/ui/public/avatar/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235.webp" "b/ui/public/avatar/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235.webp"
new file mode 100644
index 000000000..a9068097f
Binary files /dev/null and "b/ui/public/avatar/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235.webp" differ
diff --git "a/ui/public/avatar/\345\257\274\347\201\253\347\264\242.webp" "b/ui/public/avatar/\345\257\274\347\201\253\347\264\242.webp"
new file mode 100644
index 000000000..fb7fc2ee0
Binary files /dev/null and "b/ui/public/avatar/\345\257\274\347\201\253\347\264\242.webp" differ
diff --git "a/ui/public/avatar/\345\260\217\346\273\241.webp" "b/ui/public/avatar/\345\260\217\346\273\241.webp"
new file mode 100644
index 000000000..a5ea3cf47
Binary files /dev/null and "b/ui/public/avatar/\345\260\217\346\273\241.webp" differ
diff --git "a/ui/public/avatar/\345\261\261.webp" "b/ui/public/avatar/\345\261\261.webp"
new file mode 100644
index 000000000..102a7882c
Binary files /dev/null and "b/ui/public/avatar/\345\261\261.webp" differ
diff --git "a/ui/public/avatar/\345\264\226\345\277\203.webp" "b/ui/public/avatar/\345\264\226\345\277\203.webp"
new file mode 100644
index 000000000..65b5ed6aa
Binary files /dev/null and "b/ui/public/avatar/\345\264\226\345\277\203.webp" differ
diff --git "a/ui/public/avatar/\345\265\257\345\263\250.webp" "b/ui/public/avatar/\345\265\257\345\263\250.webp"
new file mode 100644
index 000000000..bbd5e8b0a
Binary files /dev/null and "b/ui/public/avatar/\345\265\257\345\263\250.webp" differ
diff --git "a/ui/public/avatar/\345\267\241\346\236\227\350\200\205.webp" "b/ui/public/avatar/\345\267\241\346\236\227\350\200\205.webp"
new file mode 100644
index 000000000..862898066
Binary files /dev/null and "b/ui/public/avatar/\345\267\241\346\236\227\350\200\205.webp" differ
diff --git "a/ui/public/avatar/\345\267\246\344\271\220.webp" "b/ui/public/avatar/\345\267\246\344\271\220.webp"
new file mode 100644
index 000000000..2e1af6a5b
Binary files /dev/null and "b/ui/public/avatar/\345\267\246\344\271\220.webp" differ
diff --git "a/ui/public/avatar/\345\267\253\346\201\213.webp" "b/ui/public/avatar/\345\267\253\346\201\213.webp"
new file mode 100644
index 000000000..38eaa1cc8
Binary files /dev/null and "b/ui/public/avatar/\345\267\253\346\201\213.webp" differ
diff --git "a/ui/public/avatar/\345\270\203\344\270\201.webp" "b/ui/public/avatar/\345\270\203\344\270\201.webp"
new file mode 100644
index 000000000..f8690236d
Binary files /dev/null and "b/ui/public/avatar/\345\270\203\344\270\201.webp" differ
diff --git "a/ui/public/avatar/\345\270\203\346\264\233\345\215\241.webp" "b/ui/public/avatar/\345\270\203\346\264\233\345\215\241.webp"
new file mode 100644
index 000000000..91f07929b
Binary files /dev/null and "b/ui/public/avatar/\345\270\203\346\264\233\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\345\270\225\346\213\211\346\226\257.webp" "b/ui/public/avatar/\345\270\225\346\213\211\346\226\257.webp"
new file mode 100644
index 000000000..71626e1a4
Binary files /dev/null and "b/ui/public/avatar/\345\270\225\346\213\211\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\345\271\264.webp" "b/ui/public/avatar/\345\271\264.webp"
new file mode 100644
index 000000000..2e7e1126d
Binary files /dev/null and "b/ui/public/avatar/\345\271\264.webp" differ
diff --git "a/ui/public/avatar/\345\271\275\347\201\265\351\262\250.webp" "b/ui/public/avatar/\345\271\275\347\201\265\351\262\250.webp"
new file mode 100644
index 000000000..3ff4b2bd0
Binary files /dev/null and "b/ui/public/avatar/\345\271\275\347\201\265\351\262\250.webp" differ
diff --git "a/ui/public/avatar/\345\274\202\345\256\242.webp" "b/ui/public/avatar/\345\274\202\345\256\242.webp"
new file mode 100644
index 000000000..59ac120b3
Binary files /dev/null and "b/ui/public/avatar/\345\274\202\345\256\242.webp" differ
diff --git "a/ui/public/avatar/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250.webp" "b/ui/public/avatar/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250.webp"
new file mode 100644
index 000000000..7787edf7e
Binary files /dev/null and "b/ui/public/avatar/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250.webp" differ
diff --git "a/ui/public/avatar/\345\276\256\351\243\216.webp" "b/ui/public/avatar/\345\276\256\351\243\216.webp"
new file mode 100644
index 000000000..79f5287aa
Binary files /dev/null and "b/ui/public/avatar/\345\276\256\351\243\216.webp" differ
diff --git "a/ui/public/avatar/\345\276\267\345\205\213\350\220\250\346\226\257.webp" "b/ui/public/avatar/\345\276\267\345\205\213\350\220\250\346\226\257.webp"
new file mode 100644
index 000000000..a8797816a
Binary files /dev/null and "b/ui/public/avatar/\345\276\267\345\205\213\350\220\250\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\346\203\212\350\233\260.webp" "b/ui/public/avatar/\346\203\212\350\233\260.webp"
new file mode 100644
index 000000000..1767481dd
Binary files /dev/null and "b/ui/public/avatar/\346\203\212\350\233\260.webp" differ
diff --git "a/ui/public/avatar/\346\205\221\347\240\202.webp" "b/ui/public/avatar/\346\205\221\347\240\202.webp"
new file mode 100644
index 000000000..cbf499b01
Binary files /dev/null and "b/ui/public/avatar/\346\205\221\347\240\202.webp" differ
diff --git "a/ui/public/avatar/\346\205\225\346\226\257.webp" "b/ui/public/avatar/\346\205\225\346\226\257.webp"
new file mode 100644
index 000000000..09fc67cc4
Binary files /dev/null and "b/ui/public/avatar/\346\205\225\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\346\210\230\350\275\246.webp" "b/ui/public/avatar/\346\210\230\350\275\246.webp"
new file mode 100644
index 000000000..1b209bc1e
Binary files /dev/null and "b/ui/public/avatar/\346\210\230\350\275\246.webp" differ
diff --git "a/ui/public/avatar/\346\210\252\344\272\221.webp" "b/ui/public/avatar/\346\210\252\344\272\221.webp"
new file mode 100644
index 000000000..15495f0db
Binary files /dev/null and "b/ui/public/avatar/\346\210\252\344\272\221.webp" differ
diff --git "a/ui/public/avatar/\346\210\264\350\217\262\346\201\251.webp" "b/ui/public/avatar/\346\210\264\350\217\262\346\201\251.webp"
new file mode 100644
index 000000000..b044ca2a1
Binary files /dev/null and "b/ui/public/avatar/\346\210\264\350\217\262\346\201\251.webp" differ
diff --git "a/ui/public/avatar/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212.webp" "b/ui/public/avatar/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212.webp"
new file mode 100644
index 000000000..7d933674a
Binary files /dev/null and "b/ui/public/avatar/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212.webp" differ
diff --git "a/ui/public/avatar/\346\212\230\345\205\211.webp" "b/ui/public/avatar/\346\212\230\345\205\211.webp"
new file mode 100644
index 000000000..6d3b1a679
Binary files /dev/null and "b/ui/public/avatar/\346\212\230\345\205\211.webp" differ
diff --git "a/ui/public/avatar/\346\213\211\346\231\256\345\205\260\345\276\267.webp" "b/ui/public/avatar/\346\213\211\346\231\256\345\205\260\345\276\267.webp"
new file mode 100644
index 000000000..010237f7a
Binary files /dev/null and "b/ui/public/avatar/\346\213\211\346\231\256\345\205\260\345\276\267.webp" differ
diff --git "a/ui/public/avatar/\346\213\234\346\235\276.webp" "b/ui/public/avatar/\346\213\234\346\235\276.webp"
new file mode 100644
index 000000000..c966deafb
Binary files /dev/null and "b/ui/public/avatar/\346\213\234\346\235\276.webp" differ
diff --git "a/ui/public/avatar/\346\216\240\351\243\216.webp" "b/ui/public/avatar/\346\216\240\351\243\216.webp"
new file mode 100644
index 000000000..af1e2f054
Binary files /dev/null and "b/ui/public/avatar/\346\216\240\351\243\216.webp" differ
diff --git "a/ui/public/avatar/\346\216\250\350\277\233\344\271\213\347\216\213.webp" "b/ui/public/avatar/\346\216\250\350\277\233\344\271\213\347\216\213.webp"
new file mode 100644
index 000000000..14054fc3e
Binary files /dev/null and "b/ui/public/avatar/\346\216\250\350\277\233\344\271\213\347\216\213.webp" differ
diff --git "a/ui/public/avatar/\346\217\220\344\270\260.webp" "b/ui/public/avatar/\346\217\220\344\270\260.webp"
new file mode 100644
index 000000000..ad11f23f7
Binary files /dev/null and "b/ui/public/avatar/\346\217\220\344\270\260.webp" differ
diff --git "a/ui/public/avatar/\346\221\251\346\240\271.webp" "b/ui/public/avatar/\346\221\251\346\240\271.webp"
new file mode 100644
index 000000000..579404227
Binary files /dev/null and "b/ui/public/avatar/\346\221\251\346\240\271.webp" differ
diff --git "a/ui/public/avatar/\346\226\221\347\202\271.webp" "b/ui/public/avatar/\346\226\221\347\202\271.webp"
new file mode 100644
index 000000000..659271284
Binary files /dev/null and "b/ui/public/avatar/\346\226\221\347\202\271.webp" differ
diff --git "a/ui/public/avatar/\346\226\245\347\275\252.webp" "b/ui/public/avatar/\346\226\245\347\275\252.webp"
new file mode 100644
index 000000000..b4873faee
Binary files /dev/null and "b/ui/public/avatar/\346\226\245\347\275\252.webp" differ
diff --git "a/ui/public/avatar/\346\226\255\345\264\226.webp" "b/ui/public/avatar/\346\226\255\345\264\226.webp"
new file mode 100644
index 000000000..2ade68a4b
Binary files /dev/null and "b/ui/public/avatar/\346\226\255\345\264\226.webp" differ
diff --git "a/ui/public/avatar/\346\226\255\347\275\252\350\200\205.webp" "b/ui/public/avatar/\346\226\255\347\275\252\350\200\205.webp"
new file mode 100644
index 000000000..dfdcc5f98
Binary files /dev/null and "b/ui/public/avatar/\346\226\255\347\275\252\350\200\205.webp" differ
diff --git "a/ui/public/avatar/\346\226\257\345\215\241\350\222\202.webp" "b/ui/public/avatar/\346\226\257\345\215\241\350\222\202.webp"
new file mode 100644
index 000000000..53c418c33
Binary files /dev/null and "b/ui/public/avatar/\346\226\257\345\215\241\350\222\202.webp" differ
diff --git "a/ui/public/avatar/\346\227\251\351\234\262.webp" "b/ui/public/avatar/\346\227\251\351\234\262.webp"
new file mode 100644
index 000000000..f09ab7057
Binary files /dev/null and "b/ui/public/avatar/\346\227\251\351\234\262.webp" differ
diff --git "a/ui/public/avatar/\346\230\216\346\244\222.webp" "b/ui/public/avatar/\346\230\216\346\244\222.webp"
new file mode 100644
index 000000000..16ce0f937
Binary files /dev/null and "b/ui/public/avatar/\346\230\216\346\244\222.webp" differ
diff --git "a/ui/public/avatar/\346\230\237\346\236\201.webp" "b/ui/public/avatar/\346\230\237\346\236\201.webp"
new file mode 100644
index 000000000..9667cea95
Binary files /dev/null and "b/ui/public/avatar/\346\230\237\346\236\201.webp" differ
diff --git "a/ui/public/avatar/\346\230\237\346\272\220.webp" "b/ui/public/avatar/\346\230\237\346\272\220.webp"
new file mode 100644
index 000000000..721f95860
Binary files /dev/null and "b/ui/public/avatar/\346\230\237\346\272\220.webp" differ
diff --git "a/ui/public/avatar/\346\230\237\347\206\212.webp" "b/ui/public/avatar/\346\230\237\347\206\212.webp"
new file mode 100644
index 000000000..b9c5433f1
Binary files /dev/null and "b/ui/public/avatar/\346\230\237\347\206\212.webp" differ
diff --git "a/ui/public/avatar/\346\231\223\346\255\214.webp" "b/ui/public/avatar/\346\231\223\346\255\214.webp"
new file mode 100644
index 000000000..5be901b0c
Binary files /dev/null and "b/ui/public/avatar/\346\231\223\346\255\214.webp" differ
diff --git "a/ui/public/avatar/\346\231\256\347\275\227\346\227\272\346\226\257.webp" "b/ui/public/avatar/\346\231\256\347\275\227\346\227\272\346\226\257.webp"
new file mode 100644
index 000000000..4128b6713
Binary files /dev/null and "b/ui/public/avatar/\346\231\256\347\275\227\346\227\272\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\346\232\227\347\264\242.webp" "b/ui/public/avatar/\346\232\227\347\264\242.webp"
new file mode 100644
index 000000000..970b288c2
Binary files /dev/null and "b/ui/public/avatar/\346\232\227\347\264\242.webp" differ
diff --git "a/ui/public/avatar/\346\232\256\350\220\275.webp" "b/ui/public/avatar/\346\232\256\350\220\275.webp"
new file mode 100644
index 000000000..5ea59886a
Binary files /dev/null and "b/ui/public/avatar/\346\232\256\350\220\275.webp" differ
diff --git "a/ui/public/avatar/\346\232\264\350\241\214.webp" "b/ui/public/avatar/\346\232\264\350\241\214.webp"
new file mode 100644
index 000000000..128eb4949
Binary files /dev/null and "b/ui/public/avatar/\346\232\264\350\241\214.webp" differ
diff --git "a/ui/public/avatar/\346\232\264\351\233\250.webp" "b/ui/public/avatar/\346\232\264\351\233\250.webp"
new file mode 100644
index 000000000..59264d295
Binary files /dev/null and "b/ui/public/avatar/\346\232\264\351\233\250.webp" differ
diff --git "a/ui/public/avatar/\346\234\210\347\246\276.webp" "b/ui/public/avatar/\346\234\210\347\246\276.webp"
new file mode 100644
index 000000000..5f014585d
Binary files /dev/null and "b/ui/public/avatar/\346\234\210\347\246\276.webp" differ
diff --git "a/ui/public/avatar/\346\234\210\350\247\201\345\244\234.webp" "b/ui/public/avatar/\346\234\210\350\247\201\345\244\234.webp"
new file mode 100644
index 000000000..9230bb488
Binary files /dev/null and "b/ui/public/avatar/\346\234\210\350\247\201\345\244\234.webp" differ
diff --git "a/ui/public/avatar/\346\234\253\350\215\257.webp" "b/ui/public/avatar/\346\234\253\350\215\257.webp"
new file mode 100644
index 000000000..e87902a47
Binary files /dev/null and "b/ui/public/avatar/\346\234\253\350\215\257.webp" differ
diff --git "a/ui/public/avatar/\346\235\217\344\273\201.webp" "b/ui/public/avatar/\346\235\217\344\273\201.webp"
new file mode 100644
index 000000000..42569d633
Binary files /dev/null and "b/ui/public/avatar/\346\235\217\344\273\201.webp" differ
diff --git "a/ui/public/avatar/\346\235\234\345\256\276.webp" "b/ui/public/avatar/\346\235\234\345\256\276.webp"
new file mode 100644
index 000000000..867e75137
Binary files /dev/null and "b/ui/public/avatar/\346\235\234\345\256\276.webp" differ
diff --git "a/ui/public/avatar/\346\235\234\346\236\227.webp" "b/ui/public/avatar/\346\235\234\346\236\227.webp"
new file mode 100644
index 000000000..60506aad2
Binary files /dev/null and "b/ui/public/avatar/\346\235\234\346\236\227.webp" differ
diff --git "a/ui/public/avatar/\346\235\260\345\205\213.webp" "b/ui/public/avatar/\346\235\260\345\205\213.webp"
new file mode 100644
index 000000000..1d6924573
Binary files /dev/null and "b/ui/public/avatar/\346\235\260\345\205\213.webp" differ
diff --git "a/ui/public/avatar/\346\235\260\350\245\277\345\215\241.webp" "b/ui/public/avatar/\346\235\260\350\245\277\345\215\241.webp"
new file mode 100644
index 000000000..7a055a2ce
Binary files /dev/null and "b/ui/public/avatar/\346\235\260\350\245\277\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\346\235\276\346\236\234.webp" "b/ui/public/avatar/\346\235\276\346\236\234.webp"
new file mode 100644
index 000000000..3a93cf8b8
Binary files /dev/null and "b/ui/public/avatar/\346\235\276\346\236\234.webp" differ
diff --git "a/ui/public/avatar/\346\236\201\345\205\211.webp" "b/ui/public/avatar/\346\236\201\345\205\211.webp"
new file mode 100644
index 000000000..4e8db8ba6
Binary files /dev/null and "b/ui/public/avatar/\346\236\201\345\205\211.webp" differ
diff --git "a/ui/public/avatar/\346\236\201\345\242\203.webp" "b/ui/public/avatar/\346\236\201\345\242\203.webp"
new file mode 100644
index 000000000..cb8df9754
Binary files /dev/null and "b/ui/public/avatar/\346\236\201\345\242\203.webp" differ
diff --git "a/ui/public/avatar/\346\236\227.webp" "b/ui/public/avatar/\346\236\227.webp"
new file mode 100644
index 000000000..4aafa0283
Binary files /dev/null and "b/ui/public/avatar/\346\236\227.webp" differ
diff --git "a/ui/public/avatar/\346\237\217\345\226\231.webp" "b/ui/public/avatar/\346\237\217\345\226\231.webp"
new file mode 100644
index 000000000..e59ed432e
Binary files /dev/null and "b/ui/public/avatar/\346\237\217\345\226\231.webp" differ
diff --git "a/ui/public/avatar/\346\240\274\345\212\263\345\205\213\346\226\257.webp" "b/ui/public/avatar/\346\240\274\345\212\263\345\205\213\346\226\257.webp"
new file mode 100644
index 000000000..951f41732
Binary files /dev/null and "b/ui/public/avatar/\346\240\274\345\212\263\345\205\213\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\346\240\274\346\213\211\345\260\274.webp" "b/ui/public/avatar/\346\240\274\346\213\211\345\260\274.webp"
new file mode 100644
index 000000000..aac15760e
Binary files /dev/null and "b/ui/public/avatar/\346\240\274\346\213\211\345\260\274.webp" differ
diff --git "a/ui/public/avatar/\346\240\274\351\233\267\344\274\212.webp" "b/ui/public/avatar/\346\240\274\351\233\267\344\274\212.webp"
new file mode 100644
index 000000000..f968db39f
Binary files /dev/null and "b/ui/public/avatar/\346\240\274\351\233\267\344\274\212.webp" differ
diff --git "a/ui/public/avatar/\346\241\203\351\207\221\345\250\230.webp" "b/ui/public/avatar/\346\241\203\351\207\221\345\250\230.webp"
new file mode 100644
index 000000000..1f7edda7a
Binary files /dev/null and "b/ui/public/avatar/\346\241\203\351\207\221\345\250\230.webp" differ
diff --git "a/ui/public/avatar/\346\241\221\350\221\232.webp" "b/ui/public/avatar/\346\241\221\350\221\232.webp"
new file mode 100644
index 000000000..a1b563be1
Binary files /dev/null and "b/ui/public/avatar/\346\241\221\350\221\232.webp" differ
diff --git "a/ui/public/avatar/\346\242\205.webp" "b/ui/public/avatar/\346\242\205.webp"
new file mode 100644
index 000000000..34e44bff9
Binary files /dev/null and "b/ui/public/avatar/\346\242\205.webp" differ
diff --git "a/ui/public/avatar/\346\242\205\345\260\224.webp" "b/ui/public/avatar/\346\242\205\345\260\224.webp"
new file mode 100644
index 000000000..6dc8ada9a
Binary files /dev/null and "b/ui/public/avatar/\346\242\205\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\346\242\223\345\205\260.webp" "b/ui/public/avatar/\346\242\223\345\205\260.webp"
new file mode 100644
index 000000000..b0a9c01d0
Binary files /dev/null and "b/ui/public/avatar/\346\242\223\345\205\260.webp" differ
diff --git "a/ui/public/avatar/\346\243\230\345\210\272.webp" "b/ui/public/avatar/\346\243\230\345\210\272.webp"
new file mode 100644
index 000000000..0ea2e5501
Binary files /dev/null and "b/ui/public/avatar/\346\243\230\345\210\272.webp" differ
diff --git "a/ui/public/avatar/\346\243\256\350\232\272.webp" "b/ui/public/avatar/\346\243\256\350\232\272.webp"
new file mode 100644
index 000000000..5df00e3f6
Binary files /dev/null and "b/ui/public/avatar/\346\243\256\350\232\272.webp" differ
diff --git "a/ui/public/avatar/\346\243\256\350\245\277.webp" "b/ui/public/avatar/\346\243\256\350\245\277.webp"
new file mode 100644
index 000000000..0783b14d1
Binary files /dev/null and "b/ui/public/avatar/\346\243\256\350\245\277.webp" differ
diff --git "a/ui/public/avatar/\346\247\220\347\220\245.webp" "b/ui/public/avatar/\346\247\220\347\220\245.webp"
new file mode 100644
index 000000000..fa4445078
Binary files /dev/null and "b/ui/public/avatar/\346\247\220\347\220\245.webp" differ
diff --git "a/ui/public/avatar/\346\255\214\350\225\276\350\222\202\345\250\205.webp" "b/ui/public/avatar/\346\255\214\350\225\276\350\222\202\345\250\205.webp"
new file mode 100644
index 000000000..8694f34ec
Binary files /dev/null and "b/ui/public/avatar/\346\255\214\350\225\276\350\222\202\345\250\205.webp" differ
diff --git "a/ui/public/avatar/\346\255\242\351\242\202.webp" "b/ui/public/avatar/\346\255\242\351\242\202.webp"
new file mode 100644
index 000000000..5aecee27e
Binary files /dev/null and "b/ui/public/avatar/\346\255\242\351\242\202.webp" differ
diff --git "a/ui/public/avatar/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267.webp" "b/ui/public/avatar/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267.webp"
new file mode 100644
index 000000000..361feb97f
Binary files /dev/null and "b/ui/public/avatar/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267.webp" differ
diff --git "a/ui/public/avatar/\346\260\264\346\234\210.webp" "b/ui/public/avatar/\346\260\264\346\234\210.webp"
new file mode 100644
index 000000000..ef5cddd3a
Binary files /dev/null and "b/ui/public/avatar/\346\260\264\346\234\210.webp" differ
diff --git "a/ui/public/avatar/\346\263\241\346\231\256\345\215\241.webp" "b/ui/public/avatar/\346\263\241\346\231\256\345\215\241.webp"
new file mode 100644
index 000000000..329a4788a
Binary files /dev/null and "b/ui/public/avatar/\346\263\241\346\231\256\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\346\263\241\346\263\241.webp" "b/ui/public/avatar/\346\263\241\346\263\241.webp"
new file mode 100644
index 000000000..2e9f2f3e6
Binary files /dev/null and "b/ui/public/avatar/\346\263\241\346\263\241.webp" differ
diff --git "a/ui/public/avatar/\346\263\242\347\231\273\345\217\257.webp" "b/ui/public/avatar/\346\263\242\347\231\273\345\217\257.webp"
new file mode 100644
index 000000000..5b2346e8a
Binary files /dev/null and "b/ui/public/avatar/\346\263\242\347\231\273\345\217\257.webp" differ
diff --git "a/ui/public/avatar/\346\263\245\345\262\251.webp" "b/ui/public/avatar/\346\263\245\345\262\251.webp"
new file mode 100644
index 000000000..3ebdad3d0
Binary files /dev/null and "b/ui/public/avatar/\346\263\245\345\262\251.webp" differ
diff --git "a/ui/public/avatar/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242.webp" "b/ui/public/avatar/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242.webp"
new file mode 100644
index 000000000..188192758
Binary files /dev/null and "b/ui/public/avatar/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242.webp" differ
diff --git "a/ui/public/avatar/\346\264\213\347\201\260.webp" "b/ui/public/avatar/\346\264\213\347\201\260.webp"
new file mode 100644
index 000000000..7b82e85be
Binary files /dev/null and "b/ui/public/avatar/\346\264\213\347\201\260.webp" differ
diff --git "a/ui/public/avatar/\346\264\233\346\264\233.webp" "b/ui/public/avatar/\346\264\233\346\264\233.webp"
new file mode 100644
index 000000000..d643cff6f
Binary files /dev/null and "b/ui/public/avatar/\346\264\233\346\264\233.webp" differ
diff --git "a/ui/public/avatar/\346\265\201\346\230\216.webp" "b/ui/public/avatar/\346\265\201\346\230\216.webp"
new file mode 100644
index 000000000..02fac4b01
Binary files /dev/null and "b/ui/public/avatar/\346\265\201\346\230\216.webp" differ
diff --git "a/ui/public/avatar/\346\265\201\346\230\237.webp" "b/ui/public/avatar/\346\265\201\346\230\237.webp"
new file mode 100644
index 000000000..b600528d0
Binary files /dev/null and "b/ui/public/avatar/\346\265\201\346\230\237.webp" differ
diff --git "a/ui/public/avatar/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202.webp" "b/ui/public/avatar/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202.webp"
new file mode 100644
index 000000000..fb47dace6
Binary files /dev/null and "b/ui/public/avatar/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202.webp" differ
diff --git "a/ui/public/avatar/\346\265\267\346\262\253.webp" "b/ui/public/avatar/\346\265\267\346\262\253.webp"
new file mode 100644
index 000000000..fa8f6aa7e
Binary files /dev/null and "b/ui/public/avatar/\346\265\267\346\262\253.webp" differ
diff --git "a/ui/public/avatar/\346\265\267\350\222\202.webp" "b/ui/public/avatar/\346\265\267\350\222\202.webp"
new file mode 100644
index 000000000..d9e7b116f
Binary files /dev/null and "b/ui/public/avatar/\346\265\267\350\222\202.webp" differ
diff --git "a/ui/public/avatar/\346\265\267\351\234\223.webp" "b/ui/public/avatar/\346\265\267\351\234\223.webp"
new file mode 100644
index 000000000..f8ffd71e1
Binary files /dev/null and "b/ui/public/avatar/\346\265\267\351\234\223.webp" differ
diff --git "a/ui/public/avatar/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241.webp" "b/ui/public/avatar/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241.webp"
new file mode 100644
index 000000000..f4888cb52
Binary files /dev/null and "b/ui/public/avatar/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\346\267\254\347\276\275\350\265\253\351\273\230.webp" "b/ui/public/avatar/\346\267\254\347\276\275\350\265\253\351\273\230.webp"
new file mode 100644
index 000000000..6aecfb8de
Binary files /dev/null and "b/ui/public/avatar/\346\267\254\347\276\275\350\265\253\351\273\230.webp" differ
diff --git "a/ui/public/avatar/\346\267\261\345\267\241.webp" "b/ui/public/avatar/\346\267\261\345\267\241.webp"
new file mode 100644
index 000000000..fc687580c
Binary files /dev/null and "b/ui/public/avatar/\346\267\261\345\267\241.webp" differ
diff --git "a/ui/public/avatar/\346\267\261\345\276\213.webp" "b/ui/public/avatar/\346\267\261\345\276\213.webp"
new file mode 100644
index 000000000..5a18440f3
Binary files /dev/null and "b/ui/public/avatar/\346\267\261\345\276\213.webp" differ
diff --git "a/ui/public/avatar/\346\267\261\346\265\267\350\211\262.webp" "b/ui/public/avatar/\346\267\261\346\265\267\350\211\262.webp"
new file mode 100644
index 000000000..dadc40ecb
Binary files /dev/null and "b/ui/public/avatar/\346\267\261\346\265\267\350\211\262.webp" differ
diff --git "a/ui/public/avatar/\346\267\261\351\235\233.webp" "b/ui/public/avatar/\346\267\261\351\235\233.webp"
new file mode 100644
index 000000000..5a46c7ec4
Binary files /dev/null and "b/ui/public/avatar/\346\267\261\351\235\233.webp" differ
diff --git "a/ui/public/avatar/\346\270\205\346\265\201.webp" "b/ui/public/avatar/\346\270\205\346\265\201.webp"
new file mode 100644
index 000000000..3b81cc793
Binary files /dev/null and "b/ui/public/avatar/\346\270\205\346\265\201.webp" differ
diff --git "a/ui/public/avatar/\346\270\205\351\201\223\345\244\253.webp" "b/ui/public/avatar/\346\270\205\351\201\223\345\244\253.webp"
new file mode 100644
index 000000000..bc04587d2
Binary files /dev/null and "b/ui/public/avatar/\346\270\205\351\201\223\345\244\253.webp" differ
diff --git "a/ui/public/avatar/\346\270\241\346\241\245.webp" "b/ui/public/avatar/\346\270\241\346\241\245.webp"
new file mode 100644
index 000000000..4be9890ef
Binary files /dev/null and "b/ui/public/avatar/\346\270\241\346\241\245.webp" differ
diff --git "a/ui/public/avatar/\346\270\251\347\261\263.webp" "b/ui/public/avatar/\346\270\251\347\261\263.webp"
new file mode 100644
index 000000000..4b0ee2423
Binary files /dev/null and "b/ui/public/avatar/\346\270\251\347\261\263.webp" differ
diff --git "a/ui/public/avatar/\346\270\251\350\222\202.webp" "b/ui/public/avatar/\346\270\251\350\222\202.webp"
new file mode 100644
index 000000000..0cae52e84
Binary files /dev/null and "b/ui/public/avatar/\346\270\251\350\222\202.webp" differ
diff --git "a/ui/public/avatar/\346\276\204\351\227\252.webp" "b/ui/public/avatar/\346\276\204\351\227\252.webp"
new file mode 100644
index 000000000..aea171bda
Binary files /dev/null and "b/ui/public/avatar/\346\276\204\351\227\252.webp" differ
diff --git "a/ui/public/avatar/\346\277\257\345\260\230\350\212\231\350\223\211.webp" "b/ui/public/avatar/\346\277\257\345\260\230\350\212\231\350\223\211.webp"
new file mode 100644
index 000000000..92bec5d80
Binary files /dev/null and "b/ui/public/avatar/\346\277\257\345\260\230\350\212\231\350\223\211.webp" differ
diff --git "a/ui/public/avatar/\347\201\253\345\223\250.webp" "b/ui/public/avatar/\347\201\253\345\223\250.webp"
new file mode 100644
index 000000000..59d7d9634
Binary files /dev/null and "b/ui/public/avatar/\347\201\253\345\223\250.webp" differ
diff --git "a/ui/public/avatar/\347\201\253\347\245\236.webp" "b/ui/public/avatar/\347\201\253\347\245\236.webp"
new file mode 100644
index 000000000..6eee989ac
Binary files /dev/null and "b/ui/public/avatar/\347\201\253\347\245\236.webp" differ
diff --git "a/ui/public/avatar/\347\201\253\351\276\231S\351\273\221\350\247\222.webp" "b/ui/public/avatar/\347\201\253\351\276\231S\351\273\221\350\247\222.webp"
new file mode 100644
index 000000000..addd86f02
Binary files /dev/null and "b/ui/public/avatar/\347\201\253\351\276\231S\351\273\221\350\247\222.webp" differ
diff --git "a/ui/public/avatar/\347\201\260\345\226\211.webp" "b/ui/public/avatar/\347\201\260\345\226\211.webp"
new file mode 100644
index 000000000..e8e37f64a
Binary files /dev/null and "b/ui/public/avatar/\347\201\260\345\226\211.webp" differ
diff --git "a/ui/public/avatar/\347\201\260\346\257\253.webp" "b/ui/public/avatar/\347\201\260\346\257\253.webp"
new file mode 100644
index 000000000..5fd651f9f
Binary files /dev/null and "b/ui/public/avatar/\347\201\260\346\257\253.webp" differ
diff --git "a/ui/public/avatar/\347\201\260\347\203\254.webp" "b/ui/public/avatar/\347\201\260\347\203\254.webp"
new file mode 100644
index 000000000..476153d44
Binary files /dev/null and "b/ui/public/avatar/\347\201\260\347\203\254.webp" differ
diff --git "a/ui/public/avatar/\347\201\265\347\237\245.webp" "b/ui/public/avatar/\347\201\265\347\237\245.webp"
new file mode 100644
index 000000000..cb9da5448
Binary files /dev/null and "b/ui/public/avatar/\347\201\265\347\237\245.webp" differ
diff --git "a/ui/public/avatar/\347\202\216\345\256\242.webp" "b/ui/public/avatar/\347\202\216\345\256\242.webp"
new file mode 100644
index 000000000..d6fd92cb4
Binary files /dev/null and "b/ui/public/avatar/\347\202\216\345\256\242.webp" differ
diff --git "a/ui/public/avatar/\347\202\216\347\206\224.webp" "b/ui/public/avatar/\347\202\216\347\206\224.webp"
new file mode 100644
index 000000000..ad5e31607
Binary files /dev/null and "b/ui/public/avatar/\347\202\216\347\206\224.webp" differ
diff --git "a/ui/public/avatar/\347\202\216\347\213\261\347\202\216\347\206\224.webp" "b/ui/public/avatar/\347\202\216\347\213\261\347\202\216\347\206\224.webp"
new file mode 100644
index 000000000..1e107bdf7
Binary files /dev/null and "b/ui/public/avatar/\347\202\216\347\213\261\347\202\216\347\206\224.webp" differ
diff --git "a/ui/public/avatar/\347\203\210\345\244\217.webp" "b/ui/public/avatar/\347\203\210\345\244\217.webp"
new file mode 100644
index 000000000..23a77e932
Binary files /dev/null and "b/ui/public/avatar/\347\203\210\345\244\217.webp" differ
diff --git "a/ui/public/avatar/\347\204\260\345\260\276.webp" "b/ui/public/avatar/\347\204\260\345\260\276.webp"
new file mode 100644
index 000000000..760410cd0
Binary files /dev/null and "b/ui/public/avatar/\347\204\260\345\260\276.webp" differ
diff --git "a/ui/public/avatar/\347\204\260\345\275\261\350\213\207\350\215\211.webp" "b/ui/public/avatar/\347\204\260\345\275\261\350\213\207\350\215\211.webp"
new file mode 100644
index 000000000..467f9084e
Binary files /dev/null and "b/ui/public/avatar/\347\204\260\345\275\261\350\213\207\350\215\211.webp" differ
diff --git "a/ui/public/avatar/\347\205\214.webp" "b/ui/public/avatar/\347\205\214.webp"
new file mode 100644
index 000000000..d6f52cec6
Binary files /dev/null and "b/ui/public/avatar/\347\205\214.webp" differ
diff --git "a/ui/public/avatar/\347\206\224\346\263\211.webp" "b/ui/public/avatar/\347\206\224\346\263\211.webp"
new file mode 100644
index 000000000..9b870afee
Binary files /dev/null and "b/ui/public/avatar/\347\206\224\346\263\211.webp" differ
diff --git "a/ui/public/avatar/\347\207\247\347\237\263.webp" "b/ui/public/avatar/\347\207\247\347\237\263.webp"
new file mode 100644
index 000000000..59d1e72ec
Binary files /dev/null and "b/ui/public/avatar/\347\207\247\347\237\263.webp" differ
diff --git "a/ui/public/avatar/\347\210\261\344\270\275\344\270\235.webp" "b/ui/public/avatar/\347\210\261\344\270\275\344\270\235.webp"
new file mode 100644
index 000000000..f320a7875
Binary files /dev/null and "b/ui/public/avatar/\347\210\261\344\270\275\344\270\235.webp" differ
diff --git "a/ui/public/avatar/\347\211\271\347\261\263\347\261\263.webp" "b/ui/public/avatar/\347\211\271\347\261\263\347\261\263.webp"
new file mode 100644
index 000000000..0d4cd6524
Binary files /dev/null and "b/ui/public/avatar/\347\211\271\347\261\263\347\261\263.webp" differ
diff --git "a/ui/public/avatar/\347\213\256\350\235\216.webp" "b/ui/public/avatar/\347\213\256\350\235\216.webp"
new file mode 100644
index 000000000..8214d7568
Binary files /dev/null and "b/ui/public/avatar/\347\213\256\350\235\216.webp" differ
diff --git "a/ui/public/avatar/\347\214\216\350\234\202.webp" "b/ui/public/avatar/\347\214\216\350\234\202.webp"
new file mode 100644
index 000000000..3fa21ad9e
Binary files /dev/null and "b/ui/public/avatar/\347\214\216\350\234\202.webp" differ
diff --git "a/ui/public/avatar/\347\216\233\346\201\251\347\272\263.webp" "b/ui/public/avatar/\347\216\233\346\201\251\347\272\263.webp"
new file mode 100644
index 000000000..05324baba
Binary files /dev/null and "b/ui/public/avatar/\347\216\233\346\201\251\347\272\263.webp" differ
diff --git "a/ui/public/avatar/\347\216\233\351\234\262\350\245\277\345\260\224.webp" "b/ui/public/avatar/\347\216\233\351\234\262\350\245\277\345\260\224.webp"
new file mode 100644
index 000000000..101811ee1
Binary files /dev/null and "b/ui/public/avatar/\347\216\233\351\234\262\350\245\277\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\347\216\253\345\205\260\350\216\216.webp" "b/ui/public/avatar/\347\216\253\345\205\260\350\216\216.webp"
new file mode 100644
index 000000000..561d861d2
Binary files /dev/null and "b/ui/public/avatar/\347\216\253\345\205\260\350\216\216.webp" differ
diff --git "a/ui/public/avatar/\347\216\253\346\213\211.webp" "b/ui/public/avatar/\347\216\253\346\213\211.webp"
new file mode 100644
index 000000000..d4c2969b8
Binary files /dev/null and "b/ui/public/avatar/\347\216\253\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205.webp" "b/ui/public/avatar/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205.webp"
new file mode 100644
index 000000000..2fa865f06
Binary files /dev/null and "b/ui/public/avatar/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205.webp" differ
diff --git "a/ui/public/avatar/\347\220\264\346\237\263.webp" "b/ui/public/avatar/\347\220\264\346\237\263.webp"
new file mode 100644
index 000000000..30ad418fc
Binary files /dev/null and "b/ui/public/avatar/\347\220\264\346\237\263.webp" differ
diff --git "a/ui/public/avatar/\347\221\225\345\205\211.webp" "b/ui/public/avatar/\347\221\225\345\205\211.webp"
new file mode 100644
index 000000000..160d45823
Binary files /dev/null and "b/ui/public/avatar/\347\221\225\345\205\211.webp" differ
diff --git "a/ui/public/avatar/\347\231\275\351\207\221.webp" "b/ui/public/avatar/\347\231\275\351\207\221.webp"
new file mode 100644
index 000000000..e1730eb2e
Binary files /dev/null and "b/ui/public/avatar/\347\231\275\351\207\221.webp" differ
diff --git "a/ui/public/avatar/\347\231\275\351\223\201.webp" "b/ui/public/avatar/\347\231\275\351\223\201.webp"
new file mode 100644
index 000000000..b57e0c9f5
Binary files /dev/null and "b/ui/public/avatar/\347\231\275\351\223\201.webp" differ
diff --git "a/ui/public/avatar/\347\231\275\351\233\252.webp" "b/ui/public/avatar/\347\231\275\351\233\252.webp"
new file mode 100644
index 000000000..a06642a56
Binary files /dev/null and "b/ui/public/avatar/\347\231\275\351\233\252.webp" differ
diff --git "a/ui/public/avatar/\347\231\275\351\235\242\351\270\256.webp" "b/ui/public/avatar/\347\231\275\351\235\242\351\270\256.webp"
new file mode 100644
index 000000000..efac3aa06
Binary files /dev/null and "b/ui/public/avatar/\347\231\275\351\235\242\351\270\256.webp" differ
diff --git "a/ui/public/avatar/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224.webp" "b/ui/public/avatar/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224.webp"
new file mode 100644
index 000000000..4efa88163
Binary files /dev/null and "b/ui/public/avatar/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\347\234\237\347\220\206.webp" "b/ui/public/avatar/\347\234\237\347\220\206.webp"
new file mode 100644
index 000000000..f751a6fe5
Binary files /dev/null and "b/ui/public/avatar/\347\234\237\347\220\206.webp" differ
diff --git "a/ui/public/avatar/\347\237\263\346\243\211.webp" "b/ui/public/avatar/\347\237\263\346\243\211.webp"
new file mode 100644
index 000000000..757e029a4
Binary files /dev/null and "b/ui/public/avatar/\347\237\263\346\243\211.webp" differ
diff --git "a/ui/public/avatar/\347\237\263\350\213\261.webp" "b/ui/public/avatar/\347\237\263\350\213\261.webp"
new file mode 100644
index 000000000..c75309f06
Binary files /dev/null and "b/ui/public/avatar/\347\237\263\350\213\261.webp" differ
diff --git "a/ui/public/avatar/\347\240\276.webp" "b/ui/public/avatar/\347\240\276.webp"
new file mode 100644
index 000000000..7f3046425
Binary files /dev/null and "b/ui/public/avatar/\347\240\276.webp" differ
diff --git "a/ui/public/avatar/\347\250\200\351\237\263.webp" "b/ui/public/avatar/\347\250\200\351\237\263.webp"
new file mode 100644
index 000000000..0f048b502
Binary files /dev/null and "b/ui/public/avatar/\347\250\200\351\237\263.webp" differ
diff --git "a/ui/public/avatar/\347\251\272.webp" "b/ui/public/avatar/\347\251\272.webp"
new file mode 100644
index 000000000..331078886
Binary files /dev/null and "b/ui/public/avatar/\347\251\272.webp" differ
diff --git "a/ui/public/avatar/\347\251\272\345\274\246.webp" "b/ui/public/avatar/\347\251\272\345\274\246.webp"
new file mode 100644
index 000000000..9e8733362
Binary files /dev/null and "b/ui/public/avatar/\347\251\272\345\274\246.webp" differ
diff --git "a/ui/public/avatar/\347\251\272\346\236\204.webp" "b/ui/public/avatar/\347\251\272\346\236\204.webp"
new file mode 100644
index 000000000..597c8493a
Binary files /dev/null and "b/ui/public/avatar/\347\251\272\346\236\204.webp" differ
diff --git "a/ui/public/avatar/\347\251\272\347\210\206.webp" "b/ui/public/avatar/\347\251\272\347\210\206.webp"
new file mode 100644
index 000000000..aa13f3262
Binary files /dev/null and "b/ui/public/avatar/\347\251\272\347\210\206.webp" differ
diff --git "a/ui/public/avatar/\347\261\263\346\240\274\351\262\201.webp" "b/ui/public/avatar/\347\261\263\346\240\274\351\262\201.webp"
new file mode 100644
index 000000000..fb506c537
Binary files /dev/null and "b/ui/public/avatar/\347\261\263\346\240\274\351\262\201.webp" differ
diff --git "a/ui/public/avatar/\347\265\256\351\233\250.webp" "b/ui/public/avatar/\347\265\256\351\233\250.webp"
new file mode 100644
index 000000000..8fe14b052
Binary files /dev/null and "b/ui/public/avatar/\347\265\256\351\233\250.webp" differ
diff --git "a/ui/public/avatar/\347\272\242.webp" "b/ui/public/avatar/\347\272\242.webp"
new file mode 100644
index 000000000..b93ae3011
Binary files /dev/null and "b/ui/public/avatar/\347\272\242.webp" differ
diff --git "a/ui/public/avatar/\347\272\242\344\272\221.webp" "b/ui/public/avatar/\347\272\242\344\272\221.webp"
new file mode 100644
index 000000000..90b8272eb
Binary files /dev/null and "b/ui/public/avatar/\347\272\242\344\272\221.webp" differ
diff --git "a/ui/public/avatar/\347\272\242\350\261\206.webp" "b/ui/public/avatar/\347\272\242\350\261\206.webp"
new file mode 100644
index 000000000..eb9d194cc
Binary files /dev/null and "b/ui/public/avatar/\347\272\242\350\261\206.webp" differ
diff --git "a/ui/public/avatar/\347\272\242\351\232\274.webp" "b/ui/public/avatar/\347\272\242\351\232\274.webp"
new file mode 100644
index 000000000..cb6a99a5b
Binary files /dev/null and "b/ui/public/avatar/\347\272\242\351\232\274.webp" differ
diff --git "a/ui/public/avatar/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211.webp" "b/ui/public/avatar/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211.webp"
new file mode 100644
index 000000000..38084a5ac
Binary files /dev/null and "b/ui/public/avatar/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\347\273\256\350\211\257.webp" "b/ui/public/avatar/\347\273\256\350\211\257.webp"
new file mode 100644
index 000000000..4d07cba9c
Binary files /dev/null and "b/ui/public/avatar/\347\273\256\350\211\257.webp" differ
diff --git "a/ui/public/avatar/\347\273\264\344\273\200\346\210\264\345\260\224.webp" "b/ui/public/avatar/\347\273\264\344\273\200\346\210\264\345\260\224.webp"
new file mode 100644
index 000000000..148effc71
Binary files /dev/null and "b/ui/public/avatar/\347\273\264\344\273\200\346\210\264\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\347\273\264\350\215\273.webp" "b/ui/public/avatar/\347\273\264\350\215\273.webp"
new file mode 100644
index 000000000..0672a34c7
Binary files /dev/null and "b/ui/public/avatar/\347\273\264\350\215\273.webp" differ
diff --git "a/ui/public/avatar/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257.webp" "b/ui/public/avatar/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257.webp"
new file mode 100644
index 000000000..0f7f900a2
Binary files /dev/null and "b/ui/public/avatar/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\347\274\240\344\270\270.webp" "b/ui/public/avatar/\347\274\240\344\270\270.webp"
new file mode 100644
index 000000000..42dbd1df9
Binary files /dev/null and "b/ui/public/avatar/\347\274\240\344\270\270.webp" differ
diff --git "a/ui/public/avatar/\347\274\252\345\260\224\350\265\233\346\200\235.webp" "b/ui/public/avatar/\347\274\252\345\260\224\350\265\233\346\200\235.webp"
new file mode 100644
index 000000000..00b563d17
Binary files /dev/null and "b/ui/public/avatar/\347\274\252\345\260\224\350\265\233\346\200\235.webp" differ
diff --git "a/ui/public/avatar/\347\275\227\345\256\276.webp" "b/ui/public/avatar/\347\275\227\345\256\276.webp"
new file mode 100644
index 000000000..bf28d215e
Binary files /dev/null and "b/ui/public/avatar/\347\275\227\345\256\276.webp" differ
diff --git "a/ui/public/avatar/\347\275\227\345\260\217\351\273\221.webp" "b/ui/public/avatar/\347\275\227\345\260\217\351\273\221.webp"
new file mode 100644
index 000000000..1e3ef5b6b
Binary files /dev/null and "b/ui/public/avatar/\347\275\227\345\260\217\351\273\221.webp" differ
diff --git "a/ui/public/avatar/\347\275\227\346\257\224\350\217\210\345\241\224.webp" "b/ui/public/avatar/\347\275\227\346\257\224\350\217\210\345\241\224.webp"
new file mode 100644
index 000000000..87d2a05c4
Binary files /dev/null and "b/ui/public/avatar/\347\275\227\346\257\224\350\217\210\345\241\224.webp" differ
diff --git "a/ui/public/avatar/\347\276\275\346\257\233\347\254\224.webp" "b/ui/public/avatar/\347\276\275\346\257\233\347\254\224.webp"
new file mode 100644
index 000000000..5ff4f3ee6
Binary files /dev/null and "b/ui/public/avatar/\347\276\275\346\257\233\347\254\224.webp" differ
diff --git "a/ui/public/avatar/\347\277\216\347\276\275.webp" "b/ui/public/avatar/\347\277\216\347\276\275.webp"
new file mode 100644
index 000000000..db03afb79
Binary files /dev/null and "b/ui/public/avatar/\347\277\216\347\276\275.webp" differ
diff --git "a/ui/public/avatar/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211.webp" "b/ui/public/avatar/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211.webp"
new file mode 100644
index 000000000..b4064646a
Binary files /dev/null and "b/ui/public/avatar/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211.webp" differ
diff --git "a/ui/public/avatar/\350\200\201\351\262\244.webp" "b/ui/public/avatar/\350\200\201\351\262\244.webp"
new file mode 100644
index 000000000..35138e95b
Binary files /dev/null and "b/ui/public/avatar/\350\200\201\351\262\244.webp" differ
diff --git "a/ui/public/avatar/\350\200\266\346\213\211.webp" "b/ui/public/avatar/\350\200\266\346\213\211.webp"
new file mode 100644
index 000000000..d38f43d93
Binary files /dev/null and "b/ui/public/avatar/\350\200\266\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\350\203\275\345\244\251\344\275\277.webp" "b/ui/public/avatar/\350\203\275\345\244\251\344\275\277.webp"
new file mode 100644
index 000000000..d41389c24
Binary files /dev/null and "b/ui/public/avatar/\350\203\275\345\244\251\344\275\277.webp" differ
diff --git "a/ui/public/avatar/\350\207\263\347\256\200.webp" "b/ui/public/avatar/\350\207\263\347\256\200.webp"
new file mode 100644
index 000000000..a7eb7f40d
Binary files /dev/null and "b/ui/public/avatar/\350\207\263\347\256\200.webp" differ
diff --git "a/ui/public/avatar/\350\211\276\344\270\235\351\273\233\345\260\224.webp" "b/ui/public/avatar/\350\211\276\344\270\235\351\273\233\345\260\224.webp"
new file mode 100644
index 000000000..bcd3730bc
Binary files /dev/null and "b/ui/public/avatar/\350\211\276\344\270\235\351\273\233\345\260\224.webp" differ
diff --git "a/ui/public/avatar/\350\211\276\344\270\275\345\246\256.webp" "b/ui/public/avatar/\350\211\276\344\270\275\345\246\256.webp"
new file mode 100644
index 000000000..0bba9b9bb
Binary files /dev/null and "b/ui/public/avatar/\350\211\276\344\270\275\345\246\256.webp" differ
diff --git "a/ui/public/avatar/\350\211\276\346\213\211.webp" "b/ui/public/avatar/\350\211\276\346\213\211.webp"
new file mode 100644
index 000000000..0119c4abe
Binary files /dev/null and "b/ui/public/avatar/\350\211\276\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\350\211\276\351\233\205\346\263\225\346\213\211.webp" "b/ui/public/avatar/\350\211\276\351\233\205\346\263\225\346\213\211.webp"
new file mode 100644
index 000000000..39f76b91c
Binary files /dev/null and "b/ui/public/avatar/\350\211\276\351\233\205\346\263\225\346\213\211.webp" differ
diff --git "a/ui/public/avatar/\350\212\231\345\205\260\345\215\241.webp" "b/ui/public/avatar/\350\212\231\345\205\260\345\215\241.webp"
new file mode 100644
index 000000000..50f36d73d
Binary files /dev/null and "b/ui/public/avatar/\350\212\231\345\205\260\345\215\241.webp" differ
diff --git "a/ui/public/avatar/\350\212\231\350\223\211.webp" "b/ui/public/avatar/\350\212\231\350\223\211.webp"
new file mode 100644
index 000000000..309946360
Binary files /dev/null and "b/ui/public/avatar/\350\212\231\350\223\211.webp" differ
diff --git "a/ui/public/avatar/\350\212\254.webp" "b/ui/public/avatar/\350\212\254.webp"
new file mode 100644
index 000000000..9cbde4e98
Binary files /dev/null and "b/ui/public/avatar/\350\212\254.webp" differ
diff --git "a/ui/public/avatar/\350\212\263\346\261\200.webp" "b/ui/public/avatar/\350\212\263\346\261\200.webp"
new file mode 100644
index 000000000..f72ecf863
Binary files /dev/null and "b/ui/public/avatar/\350\212\263\346\261\200.webp" differ
diff --git "a/ui/public/avatar/\350\213\207\350\215\211.webp" "b/ui/public/avatar/\350\213\207\350\215\211.webp"
new file mode 100644
index 000000000..edf32fdd0
Binary files /dev/null and "b/ui/public/avatar/\350\213\207\350\215\211.webp" differ
diff --git "a/ui/public/avatar/\350\213\215\350\213\224.webp" "b/ui/public/avatar/\350\213\215\350\213\224.webp"
new file mode 100644
index 000000000..f80aedf0f
Binary files /dev/null and "b/ui/public/avatar/\350\213\215\350\213\224.webp" differ
diff --git "a/ui/public/avatar/\350\213\217\350\213\217\346\264\233.webp" "b/ui/public/avatar/\350\213\217\350\213\217\346\264\233.webp"
new file mode 100644
index 000000000..2bf7b9463
Binary files /dev/null and "b/ui/public/avatar/\350\213\217\350\213\217\346\264\233.webp" differ
diff --git "a/ui/public/avatar/\350\213\246\350\211\276.webp" "b/ui/public/avatar/\350\213\246\350\211\276.webp"
new file mode 100644
index 000000000..559c3c59e
Binary files /dev/null and "b/ui/public/avatar/\350\213\246\350\211\276.webp" differ
diff --git "a/ui/public/avatar/\350\216\216\350\215\211.webp" "b/ui/public/avatar/\350\216\216\350\215\211.webp"
new file mode 100644
index 000000000..edf024a3d
Binary files /dev/null and "b/ui/public/avatar/\350\216\216\350\215\211.webp" differ
diff --git "a/ui/public/avatar/\350\216\253\346\226\257\346\217\220\351\251\254.webp" "b/ui/public/avatar/\350\216\253\346\226\257\346\217\220\351\251\254.webp"
new file mode 100644
index 000000000..f2b480bb9
Binary files /dev/null and "b/ui/public/avatar/\350\216\253\346\226\257\346\217\220\351\251\254.webp" differ
diff --git "a/ui/public/avatar/\350\216\261\344\274\212.webp" "b/ui/public/avatar/\350\216\261\344\274\212.webp"
new file mode 100644
index 000000000..5c6db6ebb
Binary files /dev/null and "b/ui/public/avatar/\350\216\261\344\274\212.webp" differ
diff --git "a/ui/public/avatar/\350\216\261\346\201\251\345\223\210\347\211\271.webp" "b/ui/public/avatar/\350\216\261\346\201\251\345\223\210\347\211\271.webp"
new file mode 100644
index 000000000..9de3c844c
Binary files /dev/null and "b/ui/public/avatar/\350\216\261\346\201\251\345\223\210\347\211\271.webp" differ
diff --git "a/ui/public/avatar/\350\216\261\346\254\247\346\226\257.webp" "b/ui/public/avatar/\350\216\261\346\254\247\346\226\257.webp"
new file mode 100644
index 000000000..1f7298b27
Binary files /dev/null and "b/ui/public/avatar/\350\216\261\346\254\247\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\350\217\262\344\272\232\346\242\205\345\241\224.webp" "b/ui/public/avatar/\350\217\262\344\272\232\346\242\205\345\241\224.webp"
new file mode 100644
index 000000000..4764e1439
Binary files /dev/null and "b/ui/public/avatar/\350\217\262\344\272\232\346\242\205\345\241\224.webp" differ
diff --git "a/ui/public/avatar/\350\223\235\346\257\222.webp" "b/ui/public/avatar/\350\223\235\346\257\222.webp"
new file mode 100644
index 000000000..ac3460feb
Binary files /dev/null and "b/ui/public/avatar/\350\223\235\346\257\222.webp" differ
diff --git "a/ui/public/avatar/\350\226\204\347\273\277.webp" "b/ui/public/avatar/\350\226\204\347\273\277.webp"
new file mode 100644
index 000000000..b1ca68bbc
Binary files /dev/null and "b/ui/public/avatar/\350\226\204\347\273\277.webp" differ
diff --git "a/ui/public/avatar/\350\226\207\350\226\207\345\256\211\345\250\234.webp" "b/ui/public/avatar/\350\226\207\350\226\207\345\256\211\345\250\234.webp"
new file mode 100644
index 000000000..4ee5e2604
Binary files /dev/null and "b/ui/public/avatar/\350\226\207\350\226\207\345\256\211\345\250\234.webp" differ
diff --git "a/ui/public/avatar/\350\232\200\346\270\205.webp" "b/ui/public/avatar/\350\232\200\346\270\205.webp"
new file mode 100644
index 000000000..b4a0cbb38
Binary files /dev/null and "b/ui/public/avatar/\350\232\200\346\270\205.webp" differ
diff --git "a/ui/public/avatar/\350\233\207\345\261\240\347\256\261.webp" "b/ui/public/avatar/\350\233\207\345\261\240\347\256\261.webp"
new file mode 100644
index 000000000..d8c9e156e
Binary files /dev/null and "b/ui/public/avatar/\350\233\207\345\261\240\347\256\261.webp" differ
diff --git "a/ui/public/avatar/\350\234\234\350\216\223.webp" "b/ui/public/avatar/\350\234\234\350\216\223.webp"
new file mode 100644
index 000000000..dc845bd41
Binary files /dev/null and "b/ui/public/avatar/\350\234\234\350\216\223.webp" differ
diff --git "a/ui/public/avatar/\350\234\234\350\234\241.webp" "b/ui/public/avatar/\350\234\234\350\234\241.webp"
new file mode 100644
index 000000000..3d1ed804a
Binary files /dev/null and "b/ui/public/avatar/\350\234\234\350\234\241.webp" differ
diff --git "a/ui/public/avatar/\350\241\241\346\262\231.webp" "b/ui/public/avatar/\350\241\241\346\262\231.webp"
new file mode 100644
index 000000000..737cb3d0f
Binary files /dev/null and "b/ui/public/avatar/\350\241\241\346\262\231.webp" differ
diff --git "a/ui/public/avatar/\350\244\220\346\236\234.webp" "b/ui/public/avatar/\350\244\220\346\236\234.webp"
new file mode 100644
index 000000000..5927645a6
Binary files /dev/null and "b/ui/public/avatar/\350\244\220\346\236\234.webp" differ
diff --git "a/ui/public/avatar/\350\247\201\350\241\214\350\200\205.webp" "b/ui/public/avatar/\350\247\201\350\241\214\350\200\205.webp"
new file mode 100644
index 000000000..e27752545
Binary files /dev/null and "b/ui/public/avatar/\350\247\201\350\241\214\350\200\205.webp" differ
diff --git "a/ui/public/avatar/\350\247\222\345\263\260.webp" "b/ui/public/avatar/\350\247\222\345\263\260.webp"
new file mode 100644
index 000000000..22cd92add
Binary files /dev/null and "b/ui/public/avatar/\350\247\222\345\263\260.webp" differ
diff --git "a/ui/public/avatar/\350\256\257\344\275\277.webp" "b/ui/public/avatar/\350\256\257\344\275\277.webp"
new file mode 100644
index 000000000..a3e7b7f31
Binary files /dev/null and "b/ui/public/avatar/\350\256\257\344\275\277.webp" differ
diff --git "a/ui/public/avatar/\350\257\227\346\200\200\351\233\205.webp" "b/ui/public/avatar/\350\257\227\346\200\200\351\233\205.webp"
new file mode 100644
index 000000000..0577cfb4b
Binary files /dev/null and "b/ui/public/avatar/\350\257\227\346\200\200\351\233\205.webp" differ
diff --git "a/ui/public/avatar/\350\260\203\351\246\231\345\270\210.webp" "b/ui/public/avatar/\350\260\203\351\246\231\345\270\210.webp"
new file mode 100644
index 000000000..a9610ada6
Binary files /dev/null and "b/ui/public/avatar/\350\260\203\351\246\231\345\270\210.webp" differ
diff --git "a/ui/public/avatar/\350\260\234\345\233\276.webp" "b/ui/public/avatar/\350\260\234\345\233\276.webp"
new file mode 100644
index 000000000..99b17726c
Binary files /dev/null and "b/ui/public/avatar/\350\260\234\345\233\276.webp" differ
diff --git "a/ui/public/avatar/\350\261\206\350\213\227.webp" "b/ui/public/avatar/\350\261\206\350\213\227.webp"
new file mode 100644
index 000000000..633ac281a
Binary files /dev/null and "b/ui/public/avatar/\350\261\206\350\213\227.webp" differ
diff --git "a/ui/public/avatar/\350\264\235\345\250\234.webp" "b/ui/public/avatar/\350\264\235\345\250\234.webp"
new file mode 100644
index 000000000..c593fe4f6
Binary files /dev/null and "b/ui/public/avatar/\350\264\235\345\250\234.webp" differ
diff --git "a/ui/public/avatar/\350\264\276\347\273\264.webp" "b/ui/public/avatar/\350\264\276\347\273\264.webp"
new file mode 100644
index 000000000..d1ccf3a1f
Binary files /dev/null and "b/ui/public/avatar/\350\264\276\347\273\264.webp" differ
diff --git "a/ui/public/avatar/\350\265\244\345\206\254.webp" "b/ui/public/avatar/\350\265\244\345\206\254.webp"
new file mode 100644
index 000000000..fd4f0bab2
Binary files /dev/null and "b/ui/public/avatar/\350\265\244\345\206\254.webp" differ
diff --git "a/ui/public/avatar/\350\265\253\345\276\267\351\233\267.webp" "b/ui/public/avatar/\350\265\253\345\276\267\351\233\267.webp"
new file mode 100644
index 000000000..9080ae1ad
Binary files /dev/null and "b/ui/public/avatar/\350\265\253\345\276\267\351\233\267.webp" differ
diff --git "a/ui/public/avatar/\350\265\253\346\213\211\346\240\274.webp" "b/ui/public/avatar/\350\265\253\346\213\211\346\240\274.webp"
new file mode 100644
index 000000000..9bf87890e
Binary files /dev/null and "b/ui/public/avatar/\350\265\253\346\213\211\346\240\274.webp" differ
diff --git "a/ui/public/avatar/\350\265\253\351\273\230.webp" "b/ui/public/avatar/\350\265\253\351\273\230.webp"
new file mode 100644
index 000000000..89091c9a1
Binary files /dev/null and "b/ui/public/avatar/\350\265\253\351\273\230.webp" differ
diff --git "a/ui/public/avatar/\350\267\203\350\267\203.webp" "b/ui/public/avatar/\350\267\203\350\267\203.webp"
new file mode 100644
index 000000000..126a5d5bc
Binary files /dev/null and "b/ui/public/avatar/\350\267\203\350\267\203.webp" differ
diff --git "a/ui/public/avatar/\350\275\246\345\260\224\345\260\274.webp" "b/ui/public/avatar/\350\275\246\345\260\224\345\260\274.webp"
new file mode 100644
index 000000000..846b9fd35
Binary files /dev/null and "b/ui/public/avatar/\350\275\246\345\260\224\345\260\274.webp" differ
diff --git "a/ui/public/avatar/\350\276\276\346\240\274\350\276\276.webp" "b/ui/public/avatar/\350\276\276\346\240\274\350\276\276.webp"
new file mode 100644
index 000000000..1568df0dd
Binary files /dev/null and "b/ui/public/avatar/\350\276\276\346\240\274\350\276\276.webp" differ
diff --git "a/ui/public/avatar/\350\277\234\345\261\261.webp" "b/ui/public/avatar/\350\277\234\345\261\261.webp"
new file mode 100644
index 000000000..583ecc275
Binary files /dev/null and "b/ui/public/avatar/\350\277\234\345\261\261.webp" differ
diff --git "a/ui/public/avatar/\350\277\234\347\211\231.webp" "b/ui/public/avatar/\350\277\234\347\211\231.webp"
new file mode 100644
index 000000000..9386be90e
Binary files /dev/null and "b/ui/public/avatar/\350\277\234\347\211\231.webp" differ
diff --git "a/ui/public/avatar/\350\277\267\350\277\255\351\246\231.webp" "b/ui/public/avatar/\350\277\267\350\277\255\351\246\231.webp"
new file mode 100644
index 000000000..b3f22cc95
Binary files /dev/null and "b/ui/public/avatar/\350\277\267\350\277\255\351\246\231.webp" differ
diff --git "a/ui/public/avatar/\351\200\201\350\221\254\344\272\272.webp" "b/ui/public/avatar/\351\200\201\350\221\254\344\272\272.webp"
new file mode 100644
index 000000000..a1b9f7524
Binary files /dev/null and "b/ui/public/avatar/\351\200\201\350\221\254\344\272\272.webp" differ
diff --git "a/ui/public/avatar/\351\200\273\345\220\204\346\226\257.webp" "b/ui/public/avatar/\351\200\273\345\220\204\346\226\257.webp"
new file mode 100644
index 000000000..6667aa573
Binary files /dev/null and "b/ui/public/avatar/\351\200\273\345\220\204\346\226\257.webp" differ
diff --git "a/ui/public/avatar/\351\205\270\347\263\226.webp" "b/ui/public/avatar/\351\205\270\347\263\226.webp"
new file mode 100644
index 000000000..2753650ac
Binary files /dev/null and "b/ui/public/avatar/\351\205\270\347\263\226.webp" differ
diff --git "a/ui/public/avatar/\351\207\215\345\262\263.webp" "b/ui/public/avatar/\351\207\215\345\262\263.webp"
new file mode 100644
index 000000000..cd34c4002
Binary files /dev/null and "b/ui/public/avatar/\351\207\215\345\262\263.webp" differ
diff --git "a/ui/public/avatar/\351\207\216\351\254\203.webp" "b/ui/public/avatar/\351\207\216\351\254\203.webp"
new file mode 100644
index 000000000..9e98d1378
Binary files /dev/null and "b/ui/public/avatar/\351\207\216\351\254\203.webp" differ
diff --git "a/ui/public/avatar/\351\223\203\345\205\260.webp" "b/ui/public/avatar/\351\223\203\345\205\260.webp"
new file mode 100644
index 000000000..4d399ba2f
Binary files /dev/null and "b/ui/public/avatar/\351\223\203\345\205\260.webp" differ
diff --git "a/ui/public/avatar/\351\223\205\350\270\235.webp" "b/ui/public/avatar/\351\223\205\350\270\235.webp"
new file mode 100644
index 000000000..6847f07fd
Binary files /dev/null and "b/ui/public/avatar/\351\223\205\350\270\235.webp" differ
diff --git "a/ui/public/avatar/\351\223\216\351\223\203.webp" "b/ui/public/avatar/\351\223\216\351\223\203.webp"
new file mode 100644
index 000000000..aa3975944
Binary files /dev/null and "b/ui/public/avatar/\351\223\216\351\223\203.webp" differ
diff --git "a/ui/public/avatar/\351\223\266\347\201\260.webp" "b/ui/public/avatar/\351\223\266\347\201\260.webp"
new file mode 100644
index 000000000..6903aaf7d
Binary files /dev/null and "b/ui/public/avatar/\351\223\266\347\201\260.webp" differ
diff --git "a/ui/public/avatar/\351\223\270\351\223\201.webp" "b/ui/public/avatar/\351\223\270\351\223\201.webp"
new file mode 100644
index 000000000..3bf7b0076
Binary files /dev/null and "b/ui/public/avatar/\351\223\270\351\223\201.webp" differ
diff --git "a/ui/public/avatar/\351\224\217.webp" "b/ui/public/avatar/\351\224\217.webp"
new file mode 100644
index 000000000..6f59bc72f
Binary files /dev/null and "b/ui/public/avatar/\351\224\217.webp" differ
diff --git "a/ui/public/avatar/\351\224\241\344\272\272.webp" "b/ui/public/avatar/\351\224\241\344\272\272.webp"
new file mode 100644
index 000000000..73502d041
Binary files /dev/null and "b/ui/public/avatar/\351\224\241\344\272\272.webp" differ
diff --git "a/ui/public/avatar/\351\224\241\345\205\260.webp" "b/ui/public/avatar/\351\224\241\345\205\260.webp"
new file mode 100644
index 000000000..b3040d393
Binary files /dev/null and "b/ui/public/avatar/\351\224\241\345\205\260.webp" differ
diff --git "a/ui/public/avatar/\351\227\252\345\207\273.webp" "b/ui/public/avatar/\351\227\252\345\207\273.webp"
new file mode 100644
index 000000000..abc38eeb3
Binary files /dev/null and "b/ui/public/avatar/\351\227\252\345\207\273.webp" differ
diff --git "a/ui/public/avatar/\351\227\252\347\201\265.webp" "b/ui/public/avatar/\351\227\252\347\201\265.webp"
new file mode 100644
index 000000000..4f253256a
Binary files /dev/null and "b/ui/public/avatar/\351\227\252\347\201\265.webp" differ
diff --git "a/ui/public/avatar/\351\230\277.webp" "b/ui/public/avatar/\351\230\277.webp"
new file mode 100644
index 000000000..708d64c3a
Binary files /dev/null and "b/ui/public/avatar/\351\230\277.webp" differ
diff --git "a/ui/public/avatar/\351\230\277\346\226\257\345\215\241\347\272\266.webp" "b/ui/public/avatar/\351\230\277\346\226\257\345\215\241\347\272\266.webp"
new file mode 100644
index 000000000..6d5b47697
Binary files /dev/null and "b/ui/public/avatar/\351\230\277\346\226\257\345\215\241\347\272\266.webp" differ
diff --git "a/ui/public/avatar/\351\230\277\346\266\210.webp" "b/ui/public/avatar/\351\230\277\346\266\210.webp"
new file mode 100644
index 000000000..04b2827bb
Binary files /dev/null and "b/ui/public/avatar/\351\230\277\346\266\210.webp" differ
diff --git "a/ui/public/avatar/\351\230\277\347\261\263\345\250\205.webp" "b/ui/public/avatar/\351\230\277\347\261\263\345\250\205.webp"
new file mode 100644
index 000000000..aaa25ee08
Binary files /dev/null and "b/ui/public/avatar/\351\230\277\347\261\263\345\250\205.webp" differ
diff --git "a/ui/public/avatar/\351\230\277\347\275\227\347\216\233.webp" "b/ui/public/avatar/\351\230\277\347\275\227\347\216\233.webp"
new file mode 100644
index 000000000..003dd99ac
Binary files /dev/null and "b/ui/public/avatar/\351\230\277\347\275\227\347\216\233.webp" differ
diff --git "a/ui/public/avatar/\351\231\210.webp" "b/ui/public/avatar/\351\231\210.webp"
new file mode 100644
index 000000000..d13733788
Binary files /dev/null and "b/ui/public/avatar/\351\231\210.webp" differ
diff --git "a/ui/public/avatar/\351\231\250\346\230\237.webp" "b/ui/public/avatar/\351\231\250\346\230\237.webp"
new file mode 100644
index 000000000..979d696ac
Binary files /dev/null and "b/ui/public/avatar/\351\231\250\346\230\237.webp" differ
diff --git "a/ui/public/avatar/\351\232\220\347\216\260.webp" "b/ui/public/avatar/\351\232\220\347\216\260.webp"
new file mode 100644
index 000000000..f04ffcaf2
Binary files /dev/null and "b/ui/public/avatar/\351\232\220\347\216\260.webp" differ
diff --git "a/ui/public/avatar/\351\233\252\347\273\222.webp" "b/ui/public/avatar/\351\233\252\347\273\222.webp"
new file mode 100644
index 000000000..7ad7846aa
Binary files /dev/null and "b/ui/public/avatar/\351\233\252\347\273\222.webp" differ
diff --git "a/ui/public/avatar/\351\233\252\351\233\211.webp" "b/ui/public/avatar/\351\233\252\351\233\211.webp"
new file mode 100644
index 000000000..d9b4dc118
Binary files /dev/null and "b/ui/public/avatar/\351\233\252\351\233\211.webp" differ
diff --git "a/ui/public/avatar/\351\233\267\350\233\207.webp" "b/ui/public/avatar/\351\233\267\350\233\207.webp"
new file mode 100644
index 000000000..96a386a05
Binary files /dev/null and "b/ui/public/avatar/\351\233\267\350\233\207.webp" differ
diff --git "a/ui/public/avatar/\351\234\215\345\260\224\346\265\267\351\233\205.webp" "b/ui/public/avatar/\351\234\215\345\260\224\346\265\267\351\233\205.webp"
new file mode 100644
index 000000000..e9c37710d
Binary files /dev/null and "b/ui/public/avatar/\351\234\215\345\260\224\346\265\267\351\233\205.webp" differ
diff --git "a/ui/public/avatar/\351\234\234\345\215\216.webp" "b/ui/public/avatar/\351\234\234\345\215\216.webp"
new file mode 100644
index 000000000..873f5f846
Binary files /dev/null and "b/ui/public/avatar/\351\234\234\345\215\216.webp" differ
diff --git "a/ui/public/avatar/\351\234\234\345\217\266.webp" "b/ui/public/avatar/\351\234\234\345\217\266.webp"
new file mode 100644
index 000000000..dfef8c35e
Binary files /dev/null and "b/ui/public/avatar/\351\234\234\345\217\266.webp" differ
diff --git "a/ui/public/avatar/\351\234\262\346\211\230.webp" "b/ui/public/avatar/\351\234\262\346\211\230.webp"
new file mode 100644
index 000000000..d40ea4632
Binary files /dev/null and "b/ui/public/avatar/\351\234\262\346\211\230.webp" differ
diff --git "a/ui/public/avatar/\351\235\222\346\236\263.webp" "b/ui/public/avatar/\351\235\222\346\236\263.webp"
new file mode 100644
index 000000000..9ee824fbd
Binary files /dev/null and "b/ui/public/avatar/\351\235\222\346\236\263.webp" differ
diff --git "a/ui/public/avatar/\351\236\255\345\210\203.webp" "b/ui/public/avatar/\351\236\255\345\210\203.webp"
new file mode 100644
index 000000000..bb4ba70d9
Binary files /dev/null and "b/ui/public/avatar/\351\236\255\345\210\203.webp" differ
diff --git "a/ui/public/avatar/\351\243\216\344\270\270.webp" "b/ui/public/avatar/\351\243\216\344\270\270.webp"
new file mode 100644
index 000000000..345205d93
Binary files /dev/null and "b/ui/public/avatar/\351\243\216\344\270\270.webp" differ
diff --git "a/ui/public/avatar/\351\243\216\347\254\233.webp" "b/ui/public/avatar/\351\243\216\347\254\233.webp"
new file mode 100644
index 000000000..0f59c296a
Binary files /dev/null and "b/ui/public/avatar/\351\243\216\347\254\233.webp" differ
diff --git "a/ui/public/avatar/\351\243\237\351\223\201\345\205\275.webp" "b/ui/public/avatar/\351\243\237\351\223\201\345\205\275.webp"
new file mode 100644
index 000000000..4bcb4dc9b
Binary files /dev/null and "b/ui/public/avatar/\351\243\237\351\223\201\345\205\275.webp" differ
diff --git "a/ui/public/avatar/\351\246\231\350\215\211.webp" "b/ui/public/avatar/\351\246\231\350\215\211.webp"
new file mode 100644
index 000000000..20b175982
Binary files /dev/null and "b/ui/public/avatar/\351\246\231\350\215\211.webp" differ
diff --git "a/ui/public/avatar/\351\255\224\347\216\213.webp" "b/ui/public/avatar/\351\255\224\347\216\213.webp"
new file mode 100644
index 000000000..ec5374fa0
Binary files /dev/null and "b/ui/public/avatar/\351\255\224\347\216\213.webp" differ
diff --git "a/ui/public/avatar/\351\270\277\351\233\252.webp" "b/ui/public/avatar/\351\270\277\351\233\252.webp"
new file mode 100644
index 000000000..d0e76cc0d
Binary files /dev/null and "b/ui/public/avatar/\351\270\277\351\233\252.webp" differ
diff --git "a/ui/public/avatar/\351\272\222\351\272\237R\345\244\234\345\210\200.webp" "b/ui/public/avatar/\351\272\222\351\272\237R\345\244\234\345\210\200.webp"
new file mode 100644
index 000000000..6c5060d7d
Binary files /dev/null and "b/ui/public/avatar/\351\272\222\351\272\237R\345\244\234\345\210\200.webp" differ
diff --git "a/ui/public/avatar/\351\272\246\345\223\262\344\274\246.webp" "b/ui/public/avatar/\351\272\246\345\223\262\344\274\246.webp"
new file mode 100644
index 000000000..101ad89d2
Binary files /dev/null and "b/ui/public/avatar/\351\272\246\345\223\262\344\274\246.webp" differ
diff --git "a/ui/public/avatar/\351\273\215.webp" "b/ui/public/avatar/\351\273\215.webp"
new file mode 100644
index 000000000..889a32b43
Binary files /dev/null and "b/ui/public/avatar/\351\273\215.webp" differ
diff --git "a/ui/public/avatar/\351\273\221.webp" "b/ui/public/avatar/\351\273\221.webp"
new file mode 100644
index 000000000..b69f8d088
Binary files /dev/null and "b/ui/public/avatar/\351\273\221.webp" differ
diff --git "a/ui/public/avatar/\351\273\221\350\247\222.webp" "b/ui/public/avatar/\351\273\221\350\247\222.webp"
new file mode 100644
index 000000000..fdde00c98
Binary files /dev/null and "b/ui/public/avatar/\351\273\221\350\247\222.webp" differ
diff --git "a/ui/public/avatar/\351\273\221\351\224\256.webp" "b/ui/public/avatar/\351\273\221\351\224\256.webp"
new file mode 100644
index 000000000..c3ff7b79a
Binary files /dev/null and "b/ui/public/avatar/\351\273\221\351\224\256.webp" differ
diff --git "a/ui/public/avatar/\351\275\220\345\260\224\346\237\245\345\205\213.webp" "b/ui/public/avatar/\351\275\220\345\260\224\346\237\245\345\205\213.webp"
new file mode 100644
index 000000000..3b0607be6
Binary files /dev/null and "b/ui/public/avatar/\351\275\220\345\260\224\346\237\245\345\205\213.webp" differ
diff --git "a/ui/public/avatar/\351\276\231\350\210\214\345\205\260.webp" "b/ui/public/avatar/\351\276\231\350\210\214\345\205\260.webp"
new file mode 100644
index 000000000..9079616e9
Binary files /dev/null and "b/ui/public/avatar/\351\276\231\350\210\214\345\205\260.webp" differ
diff --git a/ui/public/bg.webp b/ui/public/bg.webp
new file mode 100644
index 000000000..815df30c8
Binary files /dev/null and b/ui/public/bg.webp differ
diff --git a/ui/public/building_skill/[style]halo.webp b/ui/public/building_skill/[style]halo.webp
new file mode 100644
index 000000000..cda1e4003
Binary files /dev/null and b/ui/public/building_skill/[style]halo.webp differ
diff --git a/ui/public/building_skill/[style]hilight.webp b/ui/public/building_skill/[style]hilight.webp
new file mode 100644
index 000000000..06c20a9dd
Binary files /dev/null and b/ui/public/building_skill/[style]hilight.webp differ
diff --git a/ui/public/building_skill/bskil_meet_team&char.webp b/ui/public/building_skill/bskil_meet_team&char.webp
new file mode 100644
index 000000000..ba4a77dd5
Binary files /dev/null and b/ui/public/building_skill/bskil_meet_team&char.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_aegir.webp b/ui/public/building_skill/bskill_ctrl_aegir.webp
new file mode 100644
index 000000000..bc5e01df8
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_aegir.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_aegir2.webp b/ui/public/building_skill/bskill_ctrl_aegir2.webp
new file mode 100644
index 000000000..be602a44b
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_aegir2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_amiya.webp b/ui/public/building_skill/bskill_ctrl_amiya.webp
new file mode 100644
index 000000000..1e0f1cc2c
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_amiya.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_amiya1.webp b/ui/public/building_skill/bskill_ctrl_amiya1.webp
new file mode 100644
index 000000000..a0150fe82
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_amiya1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_ash.webp b/ui/public/building_skill/bskill_ctrl_ash.webp
new file mode 100644
index 000000000..a97f337b5
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_ash.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_bd_spd.webp b/ui/public/building_skill/bskill_ctrl_bd_spd.webp
new file mode 100644
index 000000000..9ffeb78a4
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_bd_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_babel.webp b/ui/public/building_skill/bskill_ctrl_c_babel.webp
new file mode 100644
index 000000000..b8e9bf7a9
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_babel.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_babel2.webp b/ui/public/building_skill/bskill_ctrl_c_babel2.webp
new file mode 100644
index 000000000..2db266c9f
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_babel2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_spd 1.webp b/ui/public/building_skill/bskill_ctrl_c_spd 1.webp
new file mode 100644
index 000000000..a6dc535f3
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_spd 1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_spd.webp b/ui/public/building_skill/bskill_ctrl_c_spd.webp
new file mode 100644
index 000000000..001eed335
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_spd1.webp b/ui/public/building_skill/bskill_ctrl_c_spd1.webp
new file mode 100644
index 000000000..331f1e0e5
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_victoria.webp b/ui/public/building_skill/bskill_ctrl_c_victoria.webp
new file mode 100644
index 000000000..39e0d5502
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_victoria.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_wt.webp b/ui/public/building_skill/bskill_ctrl_c_wt.webp
new file mode 100644
index 000000000..65eb6af0d
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_wt.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_wt1.webp b/ui/public/building_skill/bskill_ctrl_c_wt1.webp
new file mode 100644
index 000000000..1aa696247
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_wt1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_c_wt2.webp b/ui/public/building_skill/bskill_ctrl_c_wt2.webp
new file mode 100644
index 000000000..bc7899b32
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_c_wt2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_clear_sui.webp b/ui/public/building_skill/bskill_ctrl_clear_sui.webp
new file mode 100644
index 000000000..8a4de0822
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_clear_sui.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost.webp b/ui/public/building_skill/bskill_ctrl_cost.webp
new file mode 100644
index 000000000..f7803fd55
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_aegir.webp b/ui/public/building_skill/bskill_ctrl_cost_aegir.webp
new file mode 100644
index 000000000..593812db4
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_aegir.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_bd1&bd2.webp b/ui/public/building_skill/bskill_ctrl_cost_bd1&bd2.webp
new file mode 100644
index 000000000..e2812eae3
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_bd1&bd2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_bd1.webp b/ui/public/building_skill/bskill_ctrl_cost_bd1.webp
new file mode 100644
index 000000000..4dd916eb0
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_bd1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_bd2.webp b/ui/public/building_skill/bskill_ctrl_cost_bd2.webp
new file mode 100644
index 000000000..e82466389
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_bd2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_bd3.webp b/ui/public/building_skill/bskill_ctrl_cost_bd3.webp
new file mode 100644
index 000000000..274c3dbda
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_bd3.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_bd4.webp b/ui/public/building_skill/bskill_ctrl_cost_bd4.webp
new file mode 100644
index 000000000..95f881b13
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_bd4.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_expand.webp b/ui/public/building_skill/bskill_ctrl_cost_expand.webp
new file mode 100644
index 000000000..8b7d3b7f4
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_expand.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_cost_felyne.webp b/ui/public/building_skill/bskill_ctrl_cost_felyne.webp
new file mode 100644
index 000000000..94d722de2
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_cost_felyne.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_ela.webp b/ui/public/building_skill/bskill_ctrl_ela.webp
new file mode 100644
index 000000000..c7f85b499
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_ela.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_felyne.webp b/ui/public/building_skill/bskill_ctrl_felyne.webp
new file mode 100644
index 000000000..26b215065
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_felyne.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_fraction_knight.webp b/ui/public/building_skill/bskill_ctrl_fraction_knight.webp
new file mode 100644
index 000000000..902a9e7ea
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_fraction_knight.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_g_limit&spd.webp b/ui/public/building_skill/bskill_ctrl_g_limit&spd.webp
new file mode 100644
index 000000000..8e98bc50f
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_g_limit&spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_h_spd.webp b/ui/public/building_skill/bskill_ctrl_h_spd.webp
new file mode 100644
index 000000000..5870b4f47
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_h_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_karlan.webp b/ui/public/building_skill/bskill_ctrl_karlan.webp
new file mode 100644
index 000000000..f20bf3385
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_karlan.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_lda.webp b/ui/public/building_skill/bskill_ctrl_lda.webp
new file mode 100644
index 000000000..2fc5d4f2a
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_lda.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_lda_add.webp b/ui/public/building_skill/bskill_ctrl_lda_add.webp
new file mode 100644
index 000000000..e973d6aad
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_lda_add.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_leader.webp b/ui/public/building_skill/bskill_ctrl_leader.webp
new file mode 100644
index 000000000..2885e64eb
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_leader.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_lonely.webp b/ui/public/building_skill/bskill_ctrl_lonely.webp
new file mode 100644
index 000000000..2886b83db
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_lonely.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_lungmen.webp b/ui/public/building_skill/bskill_ctrl_lungmen.webp
new file mode 100644
index 000000000..196115410
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_lungmen.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_p_bot.webp b/ui/public/building_skill/bskill_ctrl_p_bot.webp
new file mode 100644
index 000000000..3d6d5d7ae
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_p_bot.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_p_spd.webp b/ui/public/building_skill/bskill_ctrl_p_spd.webp
new file mode 100644
index 000000000..c282f7f46
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_p_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_psk.webp b/ui/public/building_skill/bskill_ctrl_psk.webp
new file mode 100644
index 000000000..4a253d19e
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_psk.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_r6.webp b/ui/public/building_skill/bskill_ctrl_r6.webp
new file mode 100644
index 000000000..be66673cd
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_r6.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_single.webp b/ui/public/building_skill/bskill_ctrl_single.webp
new file mode 100644
index 000000000..b9eb5b450
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_single.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_sp.webp b/ui/public/building_skill/bskill_ctrl_sp.webp
new file mode 100644
index 000000000..ab4203e3e
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_sp.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_t_limit&spd.webp b/ui/public/building_skill/bskill_ctrl_t_limit&spd.webp
new file mode 100644
index 000000000..275d18119
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_t_limit&spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_t_spd.webp b/ui/public/building_skill/bskill_ctrl_t_spd.webp
new file mode 100644
index 000000000..487f4db8b
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_t_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_tachanka.webp b/ui/public/building_skill/bskill_ctrl_tachanka.webp
new file mode 100644
index 000000000..28b908fdd
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_tachanka.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_token_p_spd.webp b/ui/public/building_skill/bskill_ctrl_token_p_spd.webp
new file mode 100644
index 000000000..036dcbd32
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_token_p_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_token_p_spd2.webp b/ui/public/building_skill/bskill_ctrl_token_p_spd2.webp
new file mode 100644
index 000000000..51a3891a4
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_token_p_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_token_t_spd.webp b/ui/public/building_skill/bskill_ctrl_token_t_spd.webp
new file mode 100644
index 000000000..7bb7d40a8
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_token_t_spd.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_train_spd1.webp b/ui/public/building_skill/bskill_ctrl_train_spd1.webp
new file mode 100644
index 000000000..130840323
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_train_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_ctrl_ussg.webp b/ui/public/building_skill/bskill_ctrl_ussg.webp
new file mode 100644
index 000000000..2ca94aa02
Binary files /dev/null and b/ui/public/building_skill/bskill_ctrl_ussg.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&bd_n1.webp b/ui/public/building_skill/bskill_dorm_all&bd_n1.webp
new file mode 100644
index 000000000..08d07590f
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&bd_n1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&bd_n1_2.webp b/ui/public/building_skill/bskill_dorm_all&bd_n1_2.webp
new file mode 100644
index 000000000..1532ca1b8
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&bd_n1_2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&bd_n1_n2.webp b/ui/public/building_skill/bskill_dorm_all&bd_n1_n2.webp
new file mode 100644
index 000000000..adc399998
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&bd_n1_n2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&bd_n1_n3.webp b/ui/public/building_skill/bskill_dorm_all&bd_n1_n3.webp
new file mode 100644
index 000000000..a7fb26aef
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&bd_n1_n3.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&one1.webp b/ui/public/building_skill/bskill_dorm_all&one1.webp
new file mode 100644
index 000000000..cf8d451f8
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&one1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&one2.webp b/ui/public/building_skill/bskill_dorm_all&one2.webp
new file mode 100644
index 000000000..0719aded8
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&one2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&one3.webp b/ui/public/building_skill/bskill_dorm_all&one3.webp
new file mode 100644
index 000000000..0c5f2a85c
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&one3.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all&single.webp b/ui/public/building_skill/bskill_dorm_all&single.webp
new file mode 100644
index 000000000..afec1155c
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all&single.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all1.webp b/ui/public/building_skill/bskill_dorm_all1.webp
new file mode 100644
index 000000000..06c74c9a1
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all2.webp b/ui/public/building_skill/bskill_dorm_all2.webp
new file mode 100644
index 000000000..e5e608049
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all3.webp b/ui/public/building_skill/bskill_dorm_all3.webp
new file mode 100644
index 000000000..183ea70a7
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all3.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all4.webp b/ui/public/building_skill/bskill_dorm_all4.webp
new file mode 100644
index 000000000..d044b3c43
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all4.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_all_tired.webp b/ui/public/building_skill/bskill_dorm_all_tired.webp
new file mode 100644
index 000000000..236c98798
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_all_tired.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_bd_dungeon.webp b/ui/public/building_skill/bskill_dorm_bd_dungeon.webp
new file mode 100644
index 000000000..121df4e28
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_bd_dungeon.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_bdnum.webp b/ui/public/building_skill/bskill_dorm_bdnum.webp
new file mode 100644
index 000000000..0b283a37b
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_bdnum.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_exchangeAp.webp b/ui/public/building_skill/bskill_dorm_exchangeAp.webp
new file mode 100644
index 000000000..6560b0140
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_exchangeAp.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_hireToRecAll1.webp b/ui/public/building_skill/bskill_dorm_hireToRecAll1.webp
new file mode 100644
index 000000000..a2020d75d
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_hireToRecAll1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one.webp b/ui/public/building_skill/bskill_dorm_one.webp
new file mode 100644
index 000000000..b1648fcc9
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one1.webp b/ui/public/building_skill/bskill_dorm_one1.webp
new file mode 100644
index 000000000..ad01a1cfd
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one2.webp b/ui/public/building_skill/bskill_dorm_one2.webp
new file mode 100644
index 000000000..db46a9614
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one3.webp b/ui/public/building_skill/bskill_dorm_one3.webp
new file mode 100644
index 000000000..2e8ed6a3f
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one3.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one4.webp b/ui/public/building_skill/bskill_dorm_one4.webp
new file mode 100644
index 000000000..9876f8e1e
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one4.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_one5.webp b/ui/public/building_skill/bskill_dorm_one5.webp
new file mode 100644
index 000000000..1ce8a2956
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_one5.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_powToRecAll1.webp b/ui/public/building_skill/bskill_dorm_powToRecAll1.webp
new file mode 100644
index 000000000..b72927145
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_powToRecAll1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_powToRecAll2.webp b/ui/public/building_skill/bskill_dorm_powToRecAll2.webp
new file mode 100644
index 000000000..3b05d5ea9
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_powToRecAll2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_reborn.webp b/ui/public/building_skill/bskill_dorm_reborn.webp
new file mode 100644
index 000000000..1d57f85c6
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_reborn.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_rec_all&profession.webp b/ui/public/building_skill/bskill_dorm_rec_all&profession.webp
new file mode 100644
index 000000000..c3e421d14
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_rec_all&profession.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_rec_allbd.webp b/ui/public/building_skill/bskill_dorm_rec_allbd.webp
new file mode 100644
index 000000000..bdad4eb75
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_rec_allbd.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_senshi.webp b/ui/public/building_skill/bskill_dorm_senshi.webp
new file mode 100644
index 000000000..691e930c7
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_senshi.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one01.webp b/ui/public/building_skill/bskill_dorm_single&one01.webp
new file mode 100644
index 000000000..c3fcf5003
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one01.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one02.webp b/ui/public/building_skill/bskill_dorm_single&one02.webp
new file mode 100644
index 000000000..ffd75641e
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one02.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one11.webp b/ui/public/building_skill/bskill_dorm_single&one11.webp
new file mode 100644
index 000000000..18f434f76
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one11.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one12.webp b/ui/public/building_skill/bskill_dorm_single&one12.webp
new file mode 100644
index 000000000..b318cb1d8
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one12.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one21.webp b/ui/public/building_skill/bskill_dorm_single&one21.webp
new file mode 100644
index 000000000..d7d4b4130
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one21.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single&one22.webp b/ui/public/building_skill/bskill_dorm_single&one22.webp
new file mode 100644
index 000000000..7d266cc24
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single&one22.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single1.webp b/ui/public/building_skill/bskill_dorm_single1.webp
new file mode 100644
index 000000000..eda0d6810
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single1.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single2.webp b/ui/public/building_skill/bskill_dorm_single2.webp
new file mode 100644
index 000000000..0f3975743
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single2.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single3.webp b/ui/public/building_skill/bskill_dorm_single3.webp
new file mode 100644
index 000000000..2604737f6
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single3.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single4.webp b/ui/public/building_skill/bskill_dorm_single4.webp
new file mode 100644
index 000000000..aaf377b1d
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single4.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single_indigo.webp b/ui/public/building_skill/bskill_dorm_single_indigo.webp
new file mode 100644
index 000000000..63673b0b5
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single_indigo.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single_sami.webp b/ui/public/building_skill/bskill_dorm_single_sami.webp
new file mode 100644
index 000000000..64e7037b8
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single_sami.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single_schwarz.webp b/ui/public/building_skill/bskill_dorm_single_schwarz.webp
new file mode 100644
index 000000000..e57d3b795
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single_schwarz.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_single_tomimi.webp b/ui/public/building_skill/bskill_dorm_single_tomimi.webp
new file mode 100644
index 000000000..6f95edac5
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_single_tomimi.webp differ
diff --git a/ui/public/building_skill/bskill_dorm_toone1.webp b/ui/public/building_skill/bskill_dorm_toone1.webp
new file mode 100644
index 000000000..f288f3105
Binary files /dev/null and b/ui/public/building_skill/bskill_dorm_toone1.webp differ
diff --git a/ui/public/building_skill/bskill_formula_spd_sunbr.webp b/ui/public/building_skill/bskill_formula_spd_sunbr.webp
new file mode 100644
index 000000000..3929abf2c
Binary files /dev/null and b/ui/public/building_skill/bskill_formula_spd_sunbr.webp differ
diff --git a/ui/public/building_skill/bskill_hire_bd_B.webp b/ui/public/building_skill/bskill_hire_bd_B.webp
new file mode 100644
index 000000000..ca30ad158
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_bd_B.webp differ
diff --git a/ui/public/building_skill/bskill_hire_blitz.webp b/ui/public/building_skill/bskill_hire_blitz.webp
new file mode 100644
index 000000000..cf20d5759
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_blitz.webp differ
diff --git a/ui/public/building_skill/bskill_hire_skgoat.webp b/ui/public/building_skill/bskill_hire_skgoat.webp
new file mode 100644
index 000000000..1a8d5cd60
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_skgoat.webp differ
diff --git a/ui/public/building_skill/bskill_hire_skgoat2.webp b/ui/public/building_skill/bskill_hire_skgoat2.webp
new file mode 100644
index 000000000..595373454
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_skgoat2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&blacksteel2.webp b/ui/public/building_skill/bskill_hire_spd&blacksteel2.webp
new file mode 100644
index 000000000..012d94ceb
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&blacksteel2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&clue.webp b/ui/public/building_skill/bskill_hire_spd&clue.webp
new file mode 100644
index 000000000..ae9cc9d57
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&clue.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&clue2.webp b/ui/public/building_skill/bskill_hire_spd&clue2.webp
new file mode 100644
index 000000000..7485efa90
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&clue2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost1.webp b/ui/public/building_skill/bskill_hire_spd&cost1.webp
new file mode 100644
index 000000000..0140c0a4b
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost1.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost2.webp b/ui/public/building_skill/bskill_hire_spd&cost2.webp
new file mode 100644
index 000000000..27ecff89e
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost3.webp b/ui/public/building_skill/bskill_hire_spd&cost3.webp
new file mode 100644
index 000000000..1398a6e1a
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost3.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost4.webp b/ui/public/building_skill/bskill_hire_spd&cost4.webp
new file mode 100644
index 000000000..1527c549c
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost4.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost5.webp b/ui/public/building_skill/bskill_hire_spd&cost5.webp
new file mode 100644
index 000000000..ec6b8a437
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost5.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&cost6.webp b/ui/public/building_skill/bskill_hire_spd&cost6.webp
new file mode 100644
index 000000000..6ad1cc03a
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&cost6.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&dorm1.webp b/ui/public/building_skill/bskill_hire_spd&dorm1.webp
new file mode 100644
index 000000000..55774ba6f
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&dorm1.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&dorm2.webp b/ui/public/building_skill/bskill_hire_spd&dorm2.webp
new file mode 100644
index 000000000..fbca249fd
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&dorm2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&extra1.webp b/ui/public/building_skill/bskill_hire_spd&extra1.webp
new file mode 100644
index 000000000..04b8bfb36
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&extra1.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&glasgow.webp b/ui/public/building_skill/bskill_hire_spd&glasgow.webp
new file mode 100644
index 000000000..0289704ba
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&glasgow.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd&ursus2.webp b/ui/public/building_skill/bskill_hire_spd&ursus2.webp
new file mode 100644
index 000000000..b6ae8c0a0
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd&ursus2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd1.webp b/ui/public/building_skill/bskill_hire_spd1.webp
new file mode 100644
index 000000000..f445d01a1
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd2.webp b/ui/public/building_skill/bskill_hire_spd2.webp
new file mode 100644
index 000000000..d63b7d761
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd3.webp b/ui/public/building_skill/bskill_hire_spd3.webp
new file mode 100644
index 000000000..6430d4f34
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd4.webp b/ui/public/building_skill/bskill_hire_spd4.webp
new file mode 100644
index 000000000..4def143b6
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd4.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd5.webp b/ui/public/building_skill/bskill_hire_spd5.webp
new file mode 100644
index 000000000..ef98ee261
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd5.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd_bd_n1_n1.webp b/ui/public/building_skill/bskill_hire_spd_bd_n1_n1.webp
new file mode 100644
index 000000000..6d1a264df
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd_bd_n1_n1.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd_bd_n2.webp b/ui/public/building_skill/bskill_hire_spd_bd_n2.webp
new file mode 100644
index 000000000..a82116b0f
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd_bd_n2.webp differ
diff --git a/ui/public/building_skill/bskill_hire_spd_memento.webp b/ui/public/building_skill/bskill_hire_spd_memento.webp
new file mode 100644
index 000000000..e4261a4c7
Binary files /dev/null and b/ui/public/building_skill/bskill_hire_spd_memento.webp differ
diff --git a/ui/public/building_skill/bskill_man_A1.webp b/ui/public/building_skill/bskill_man_A1.webp
new file mode 100644
index 000000000..f1e23e455
Binary files /dev/null and b/ui/public/building_skill/bskill_man_A1.webp differ
diff --git a/ui/public/building_skill/bskill_man_bd1.webp b/ui/public/building_skill/bskill_man_bd1.webp
new file mode 100644
index 000000000..16b63a107
Binary files /dev/null and b/ui/public/building_skill/bskill_man_bd1.webp differ
diff --git a/ui/public/building_skill/bskill_man_constrLv.webp b/ui/public/building_skill/bskill_man_constrLv.webp
new file mode 100644
index 000000000..30eafe939
Binary files /dev/null and b/ui/public/building_skill/bskill_man_constrLv.webp differ
diff --git a/ui/public/building_skill/bskill_man_cost_all.webp b/ui/public/building_skill/bskill_man_cost_all.webp
new file mode 100644
index 000000000..7d3de0b04
Binary files /dev/null and b/ui/public/building_skill/bskill_man_cost_all.webp differ
diff --git a/ui/public/building_skill/bskill_man_cost_room1.webp b/ui/public/building_skill/bskill_man_cost_room1.webp
new file mode 100644
index 000000000..46bdfdb1e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_cost_room1.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp&cost.webp b/ui/public/building_skill/bskill_man_exp&cost.webp
new file mode 100644
index 000000000..e319f73a0
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp&cost.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp&limit.webp b/ui/public/building_skill/bskill_man_exp&limit.webp
new file mode 100644
index 000000000..b24ac085f
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp&limit.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp&limit1.webp b/ui/public/building_skill/bskill_man_exp&limit1.webp
new file mode 100644
index 000000000..52c588996
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp&limit1.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp&limit2.webp b/ui/public/building_skill/bskill_man_exp&limit2.webp
new file mode 100644
index 000000000..5ea41fdf4
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp&limit2.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp&limit3.webp b/ui/public/building_skill/bskill_man_exp&limit3.webp
new file mode 100644
index 000000000..70bad3b9a
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp&limit3.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp1.webp b/ui/public/building_skill/bskill_man_exp1.webp
new file mode 100644
index 000000000..7c17aabd6
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp1.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp2.webp b/ui/public/building_skill/bskill_man_exp2.webp
new file mode 100644
index 000000000..5892a4af1
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp2.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp3.webp b/ui/public/building_skill/bskill_man_exp3.webp
new file mode 100644
index 000000000..71837b34e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp3.webp differ
diff --git a/ui/public/building_skill/bskill_man_exp4.webp b/ui/public/building_skill/bskill_man_exp4.webp
new file mode 100644
index 000000000..a8cebcee4
Binary files /dev/null and b/ui/public/building_skill/bskill_man_exp4.webp differ
diff --git a/ui/public/building_skill/bskill_man_fuze.webp b/ui/public/building_skill/bskill_man_fuze.webp
new file mode 100644
index 000000000..b06a6cf57
Binary files /dev/null and b/ui/public/building_skill/bskill_man_fuze.webp differ
diff --git a/ui/public/building_skill/bskill_man_gold&blacksteel.webp b/ui/public/building_skill/bskill_man_gold&blacksteel.webp
new file mode 100644
index 000000000..6afad03bf
Binary files /dev/null and b/ui/public/building_skill/bskill_man_gold&blacksteel.webp differ
diff --git a/ui/public/building_skill/bskill_man_gold1.webp b/ui/public/building_skill/bskill_man_gold1.webp
new file mode 100644
index 000000000..5e2244985
Binary files /dev/null and b/ui/public/building_skill/bskill_man_gold1.webp differ
diff --git a/ui/public/building_skill/bskill_man_gold2.webp b/ui/public/building_skill/bskill_man_gold2.webp
new file mode 100644
index 000000000..24c647842
Binary files /dev/null and b/ui/public/building_skill/bskill_man_gold2.webp differ
diff --git a/ui/public/building_skill/bskill_man_gold3.webp b/ui/public/building_skill/bskill_man_gold3.webp
new file mode 100644
index 000000000..082aeebbe
Binary files /dev/null and b/ui/public/building_skill/bskill_man_gold3.webp differ
diff --git a/ui/public/building_skill/bskill_man_gold4.webp b/ui/public/building_skill/bskill_man_gold4.webp
new file mode 100644
index 000000000..26625696c
Binary files /dev/null and b/ui/public/building_skill/bskill_man_gold4.webp differ
diff --git a/ui/public/building_skill/bskill_man_limit&cost.webp b/ui/public/building_skill/bskill_man_limit&cost.webp
new file mode 100644
index 000000000..87d77b148
Binary files /dev/null and b/ui/public/building_skill/bskill_man_limit&cost.webp differ
diff --git a/ui/public/building_skill/bskill_man_limit&cost1.webp b/ui/public/building_skill/bskill_man_limit&cost1.webp
new file mode 100644
index 000000000..e10de6651
Binary files /dev/null and b/ui/public/building_skill/bskill_man_limit&cost1.webp differ
diff --git a/ui/public/building_skill/bskill_man_limit&cost2.webp b/ui/public/building_skill/bskill_man_limit&cost2.webp
new file mode 100644
index 000000000..278ae1cec
Binary files /dev/null and b/ui/public/building_skill/bskill_man_limit&cost2.webp differ
diff --git a/ui/public/building_skill/bskill_man_limit&cost3.webp b/ui/public/building_skill/bskill_man_limit&cost3.webp
new file mode 100644
index 000000000..f2c8059e3
Binary files /dev/null and b/ui/public/building_skill/bskill_man_limit&cost3.webp differ
diff --git a/ui/public/building_skill/bskill_man_limit&cost5.webp b/ui/public/building_skill/bskill_man_limit&cost5.webp
new file mode 100644
index 000000000..3cfeea1c9
Binary files /dev/null and b/ui/public/building_skill/bskill_man_limit&cost5.webp differ
diff --git a/ui/public/building_skill/bskill_man_marcille1.webp b/ui/public/building_skill/bskill_man_marcille1.webp
new file mode 100644
index 000000000..e590b9100
Binary files /dev/null and b/ui/public/building_skill/bskill_man_marcille1.webp differ
diff --git a/ui/public/building_skill/bskill_man_marcille2.webp b/ui/public/building_skill/bskill_man_marcille2.webp
new file mode 100644
index 000000000..389ccad2b
Binary files /dev/null and b/ui/public/building_skill/bskill_man_marcille2.webp differ
diff --git a/ui/public/building_skill/bskill_man_originium1.webp b/ui/public/building_skill/bskill_man_originium1.webp
new file mode 100644
index 000000000..7301eee01
Binary files /dev/null and b/ui/public/building_skill/bskill_man_originium1.webp differ
diff --git a/ui/public/building_skill/bskill_man_originium2.webp b/ui/public/building_skill/bskill_man_originium2.webp
new file mode 100644
index 000000000..6d6389cc0
Binary files /dev/null and b/ui/public/building_skill/bskill_man_originium2.webp differ
diff --git a/ui/public/building_skill/bskill_man_skill_change.webp b/ui/public/building_skill/bskill_man_skill_change.webp
new file mode 100644
index 000000000..2d5cd2ad3
Binary files /dev/null and b/ui/public/building_skill/bskill_man_skill_change.webp differ
diff --git a/ui/public/building_skill/bskill_man_skill_spd.webp b/ui/public/building_skill/bskill_man_skill_spd.webp
new file mode 100644
index 000000000..b28fb926f
Binary files /dev/null and b/ui/public/building_skill/bskill_man_skill_spd.webp differ
diff --git a/ui/public/building_skill/bskill_man_skill_spd2.webp b/ui/public/building_skill/bskill_man_skill_spd2.webp
new file mode 100644
index 000000000..29480ae29
Binary files /dev/null and b/ui/public/building_skill/bskill_man_skill_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_man_skill_spd3.webp b/ui/public/building_skill/bskill_man_skill_spd3.webp
new file mode 100644
index 000000000..a4aadbbbc
Binary files /dev/null and b/ui/public/building_skill/bskill_man_skill_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&dorm1.webp b/ui/public/building_skill/bskill_man_spd&dorm1.webp
new file mode 100644
index 000000000..e5ed2e7a2
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&dorm1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit&cost1.webp b/ui/public/building_skill/bskill_man_spd&limit&cost1.webp
new file mode 100644
index 000000000..acf81ef15
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit&cost1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit&cost2.webp b/ui/public/building_skill/bskill_man_spd&limit&cost2.webp
new file mode 100644
index 000000000..5102ea7b0
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit&cost2.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit&cost3.webp b/ui/public/building_skill/bskill_man_spd&limit&cost3.webp
new file mode 100644
index 000000000..06c5ddf25
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit&cost3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit&cost4.webp b/ui/public/building_skill/bskill_man_spd&limit&cost4.webp
new file mode 100644
index 000000000..6712a704e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit&cost4.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit1.webp b/ui/public/building_skill/bskill_man_spd&limit1.webp
new file mode 100644
index 000000000..06d3fc413
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit3.webp b/ui/public/building_skill/bskill_man_spd&limit3.webp
new file mode 100644
index 000000000..2480b780d
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&limit_felyne.webp b/ui/public/building_skill/bskill_man_spd&limit_felyne.webp
new file mode 100644
index 000000000..826813f22
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&limit_felyne.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&power1.webp b/ui/public/building_skill/bskill_man_spd&power1.webp
new file mode 100644
index 000000000..7a08d8649
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&power1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&power2.webp b/ui/public/building_skill/bskill_man_spd&power2.webp
new file mode 100644
index 000000000..3e251c37e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&power2.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&power3.webp b/ui/public/building_skill/bskill_man_spd&power3.webp
new file mode 100644
index 000000000..ea7bc8dd3
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&power3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd&trade.webp b/ui/public/building_skill/bskill_man_spd&trade.webp
new file mode 100644
index 000000000..68d165232
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd&trade.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd1.webp b/ui/public/building_skill/bskill_man_spd1.webp
new file mode 100644
index 000000000..12bdb3e49
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd2.webp b/ui/public/building_skill/bskill_man_spd2.webp
new file mode 100644
index 000000000..92a24c7a7
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd3.webp b/ui/public/building_skill/bskill_man_spd3.webp
new file mode 100644
index 000000000..bd790f876
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_add&cost.webp b/ui/public/building_skill/bskill_man_spd_add&cost.webp
new file mode 100644
index 000000000..0ed7b430e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_add&cost.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_add1.webp b/ui/public/building_skill/bskill_man_spd_add1.webp
new file mode 100644
index 000000000..66c35dcb3
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_add1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_add2.webp b/ui/public/building_skill/bskill_man_spd_add2.webp
new file mode 100644
index 000000000..289386a63
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_add2.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_add3.webp b/ui/public/building_skill/bskill_man_spd_add3.webp
new file mode 100644
index 000000000..126164465
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_add3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd1.webp b/ui/public/building_skill/bskill_man_spd_bd1.webp
new file mode 100644
index 000000000..f97a9045d
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd2.webp b/ui/public/building_skill/bskill_man_spd_bd2.webp
new file mode 100644
index 000000000..82e5970f1
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd2.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd3.webp b/ui/public/building_skill/bskill_man_spd_bd3.webp
new file mode 100644
index 000000000..8ec7a717c
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd3.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd4.webp b/ui/public/building_skill/bskill_man_spd_bd4.webp
new file mode 100644
index 000000000..739281a85
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd4.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd5.webp b/ui/public/building_skill/bskill_man_spd_bd5.webp
new file mode 100644
index 000000000..f0397da0c
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd5.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd6.webp b/ui/public/building_skill/bskill_man_spd_bd6.webp
new file mode 100644
index 000000000..277908b71
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd6.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd7.webp b/ui/public/building_skill/bskill_man_spd_bd7.webp
new file mode 100644
index 000000000..dcce08d45
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd7.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd_dungeon.webp b/ui/public/building_skill/bskill_man_spd_bd_dungeon.webp
new file mode 100644
index 000000000..24b241925
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd_dungeon.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_bd_n1.webp b/ui/public/building_skill/bskill_man_spd_bd_n1.webp
new file mode 100644
index 000000000..0de8b5269
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_bd_n1.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_reduce.webp b/ui/public/building_skill/bskill_man_spd_reduce.webp
new file mode 100644
index 000000000..ad59bb036
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_reduce.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_variable11.webp b/ui/public/building_skill/bskill_man_spd_variable11.webp
new file mode 100644
index 000000000..32595180e
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_variable11.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_variable21.webp b/ui/public/building_skill/bskill_man_spd_variable21.webp
new file mode 100644
index 000000000..81a115729
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_variable21.webp differ
diff --git a/ui/public/building_skill/bskill_man_spd_variable31.webp b/ui/public/building_skill/bskill_man_spd_variable31.webp
new file mode 100644
index 000000000..03d98facc
Binary files /dev/null and b/ui/public/building_skill/bskill_man_spd_variable31.webp differ
diff --git a/ui/public/building_skill/bskill_meet_bd_dungeon.webp b/ui/public/building_skill/bskill_meet_bd_dungeon.webp
new file mode 100644
index 000000000..4b666636e
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_bd_dungeon.webp differ
diff --git a/ui/public/building_skill/bskill_meet_blacksteel2.webp b/ui/public/building_skill/bskill_meet_blacksteel2.webp
new file mode 100644
index 000000000..b155579e7
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_blacksteel2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_condChar.webp b/ui/public/building_skill/bskill_meet_condChar.webp
new file mode 100644
index 000000000..dc5b61830
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_condChar.webp differ
diff --git a/ui/public/building_skill/bskill_meet_exchange.webp b/ui/public/building_skill/bskill_meet_exchange.webp
new file mode 100644
index 000000000..600a4478b
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_exchange.webp differ
diff --git a/ui/public/building_skill/bskill_meet_flag_glasgow.webp b/ui/public/building_skill/bskill_meet_flag_glasgow.webp
new file mode 100644
index 000000000..cff2cf1df
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_flag_glasgow.webp differ
diff --git a/ui/public/building_skill/bskill_meet_flag_kjerag.webp b/ui/public/building_skill/bskill_meet_flag_kjerag.webp
new file mode 100644
index 000000000..db27e1513
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_flag_kjerag.webp differ
diff --git a/ui/public/building_skill/bskill_meet_flag_rhine.webp b/ui/public/building_skill/bskill_meet_flag_rhine.webp
new file mode 100644
index 000000000..5fac750c6
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_flag_rhine.webp differ
diff --git a/ui/public/building_skill/bskill_meet_flag_rhodes.webp b/ui/public/building_skill/bskill_meet_flag_rhodes.webp
new file mode 100644
index 000000000..9baff07cb
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_flag_rhodes.webp differ
diff --git a/ui/public/building_skill/bskill_meet_flag_ursus.webp b/ui/public/building_skill/bskill_meet_flag_ursus.webp
new file mode 100644
index 000000000..47658c2ce
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_flag_ursus.webp differ
diff --git a/ui/public/building_skill/bskill_meet_glasgow1.webp b/ui/public/building_skill/bskill_meet_glasgow1.webp
new file mode 100644
index 000000000..7cbd0da42
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_glasgow1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_glasgow2.webp b/ui/public/building_skill/bskill_meet_glasgow2.webp
new file mode 100644
index 000000000..7c5d8ca40
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_glasgow2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_iana.webp b/ui/public/building_skill/bskill_meet_iana.webp
new file mode 100644
index 000000000..80fb3ff2b
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_iana.webp differ
diff --git a/ui/public/building_skill/bskill_meet_kjerag2.webp b/ui/public/building_skill/bskill_meet_kjerag2.webp
new file mode 100644
index 000000000..ddfe81817
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_kjerag2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_penguin1.webp b/ui/public/building_skill/bskill_meet_penguin1.webp
new file mode 100644
index 000000000..d27f341fa
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_penguin1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_penguin2.webp b/ui/public/building_skill/bskill_meet_penguin2.webp
new file mode 100644
index 000000000..af5f7e31f
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_penguin2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_rhine2.webp b/ui/public/building_skill/bskill_meet_rhine2.webp
new file mode 100644
index 000000000..84e5d435e
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_rhine2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_rhodes1.webp b/ui/public/building_skill/bskill_meet_rhodes1.webp
new file mode 100644
index 000000000..927a1c720
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_rhodes1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_rhodes2.webp b/ui/public/building_skill/bskill_meet_rhodes2.webp
new file mode 100644
index 000000000..e2cb5a3bb
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_rhodes2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&bd¬Owned.webp b/ui/public/building_skill/bskill_meet_spd&bd¬Owned.webp
new file mode 100644
index 000000000..64f084868
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&bd¬Owned.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&bd.webp b/ui/public/building_skill/bskill_meet_spd&bd.webp
new file mode 100644
index 000000000..6d6f5c94c
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&bd.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&condChar_mustget.webp b/ui/public/building_skill/bskill_meet_spd&condChar_mustget.webp
new file mode 100644
index 000000000..4a98a0701
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&condChar_mustget.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&condChar_mustget2.webp b/ui/public/building_skill/bskill_meet_spd&condChar_mustget2.webp
new file mode 100644
index 000000000..53c4449df
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&condChar_mustget2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&cost.webp b/ui/public/building_skill/bskill_meet_spd&cost.webp
new file mode 100644
index 000000000..81e55b153
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&cost.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&cost_condChar.webp b/ui/public/building_skill/bskill_meet_spd&cost_condChar.webp
new file mode 100644
index 000000000..f41b0fbdb
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&cost_condChar.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&cost_condChar2.webp b/ui/public/building_skill/bskill_meet_spd&cost_condChar2.webp
new file mode 100644
index 000000000..23ec35287
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&cost_condChar2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&cost_condChar3.webp b/ui/public/building_skill/bskill_meet_spd&cost_condChar3.webp
new file mode 100644
index 000000000..d3a8502e1
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&cost_condChar3.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd&sami.webp b/ui/public/building_skill/bskill_meet_spd&sami.webp
new file mode 100644
index 000000000..781206716
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd&sami.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd1.webp b/ui/public/building_skill/bskill_meet_spd1.webp
new file mode 100644
index 000000000..24b2fe5bc
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd2.webp b/ui/public/building_skill/bskill_meet_spd2.webp
new file mode 100644
index 000000000..6334ba245
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd3.webp b/ui/public/building_skill/bskill_meet_spd3.webp
new file mode 100644
index 000000000..f92f6b394
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd4.webp b/ui/public/building_skill/bskill_meet_spd4.webp
new file mode 100644
index 000000000..26eb9002d
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd4.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spdNotOwned1.webp b/ui/public/building_skill/bskill_meet_spdNotOwned1.webp
new file mode 100644
index 000000000..ae7f898bc
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spdNotOwned1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spdNotOwned2.webp b/ui/public/building_skill/bskill_meet_spdNotOwned2.webp
new file mode 100644
index 000000000..528b4b1da
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spdNotOwned2.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spdOwned1.webp b/ui/public/building_skill/bskill_meet_spdOwned1.webp
new file mode 100644
index 000000000..a563bcfaa
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spdOwned1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_spd_hast1.webp b/ui/public/building_skill/bskill_meet_spd_hast1.webp
new file mode 100644
index 000000000..010bb7238
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_spd_hast1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_ursus1.webp b/ui/public/building_skill/bskill_meet_ursus1.webp
new file mode 100644
index 000000000..419261685
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_ursus1.webp differ
diff --git a/ui/public/building_skill/bskill_meet_ursus2.webp b/ui/public/building_skill/bskill_meet_ursus2.webp
new file mode 100644
index 000000000..d6ba8e9d9
Binary files /dev/null and b/ui/public/building_skill/bskill_meet_ursus2.webp differ
diff --git a/ui/public/building_skill/bskill_pow_count.webp b/ui/public/building_skill/bskill_pow_count.webp
new file mode 100644
index 000000000..8c4196829
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_count.webp differ
diff --git a/ui/public/building_skill/bskill_pow_drone.webp b/ui/public/building_skill/bskill_pow_drone.webp
new file mode 100644
index 000000000..3c3054db8
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_drone.webp differ
diff --git a/ui/public/building_skill/bskill_pow_jnight.webp b/ui/public/building_skill/bskill_pow_jnight.webp
new file mode 100644
index 000000000..440cd2e46
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_jnight.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd&cost.webp b/ui/public/building_skill/bskill_pow_spd&cost.webp
new file mode 100644
index 000000000..34dec83d4
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd&cost.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd1.webp b/ui/public/building_skill/bskill_pow_spd1.webp
new file mode 100644
index 000000000..890876a64
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd2.webp b/ui/public/building_skill/bskill_pow_spd2.webp
new file mode 100644
index 000000000..780429720
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd3.webp b/ui/public/building_skill/bskill_pow_spd3.webp
new file mode 100644
index 000000000..e05be7504
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd_P.webp b/ui/public/building_skill/bskill_pow_spd_P.webp
new file mode 100644
index 000000000..22b0e9c06
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd_P.webp differ
diff --git a/ui/public/building_skill/bskill_pow_spd_P1.webp b/ui/public/building_skill/bskill_pow_spd_P1.webp
new file mode 100644
index 000000000..5479c3ce0
Binary files /dev/null and b/ui/public/building_skill/bskill_pow_spd_P1.webp differ
diff --git a/ui/public/building_skill/bskill_power_rec_rhine.webp b/ui/public/building_skill/bskill_power_rec_rhine.webp
new file mode 100644
index 000000000..84a718fe2
Binary files /dev/null and b/ui/public/building_skill/bskill_power_rec_rhine.webp differ
diff --git a/ui/public/building_skill/bskill_power_rec_spd&addition1.webp b/ui/public/building_skill/bskill_power_rec_spd&addition1.webp
new file mode 100644
index 000000000..7ae4ce4d5
Binary files /dev/null and b/ui/public/building_skill/bskill_power_rec_spd&addition1.webp differ
diff --git a/ui/public/building_skill/bskill_power_rec_spd&addition2.webp b/ui/public/building_skill/bskill_power_rec_spd&addition2.webp
new file mode 100644
index 000000000..84c9f852e
Binary files /dev/null and b/ui/public/building_skill/bskill_power_rec_spd&addition2.webp differ
diff --git a/ui/public/building_skill/bskill_tra-spd&dorm1.webp b/ui/public/building_skill/bskill_tra-spd&dorm1.webp
new file mode 100644
index 000000000..02d189bdd
Binary files /dev/null and b/ui/public/building_skill/bskill_tra-spd&dorm1.webp differ
diff --git a/ui/public/building_skill/bskill_tra-spd&dorm2.webp b/ui/public/building_skill/bskill_tra-spd&dorm2.webp
new file mode 100644
index 000000000..945acfd83
Binary files /dev/null and b/ui/public/building_skill/bskill_tra-spd&dorm2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_Lappland1.webp b/ui/public/building_skill/bskill_tra_Lappland1.webp
new file mode 100644
index 000000000..9b320191f
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_Lappland1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_Lappland2.webp b/ui/public/building_skill/bskill_tra_Lappland2.webp
new file mode 100644
index 000000000..65e50afc9
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_Lappland2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_against.webp b/ui/public/building_skill/bskill_tra_against.webp
new file mode 100644
index 000000000..99705014d
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_against.webp differ
diff --git a/ui/public/building_skill/bskill_tra_against2.webp b/ui/public/building_skill/bskill_tra_against2.webp
new file mode 100644
index 000000000..fa7038e91
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_against2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_bd_cost1.webp b/ui/public/building_skill/bskill_tra_bd_cost1.webp
new file mode 100644
index 000000000..7ecaa9cbd
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_bd_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_bd_cost2.webp b/ui/public/building_skill/bskill_tra_bd_cost2.webp
new file mode 100644
index 000000000..557cdb037
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_bd_cost2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_bd_n1.webp b/ui/public/building_skill/bskill_tra_bd_n1.webp
new file mode 100644
index 000000000..22ac34c6e
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_bd_n1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_bd_n2.webp b/ui/public/building_skill/bskill_tra_bd_n2.webp
new file mode 100644
index 000000000..0854f9426
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_bd_n2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_cost1.webp b/ui/public/building_skill/bskill_tra_cost1.webp
new file mode 100644
index 000000000..e9980f4ba
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_durin.webp b/ui/public/building_skill/bskill_tra_flow_durin.webp
new file mode 100644
index 000000000..5d01bd857
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_durin.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_gc1.webp b/ui/public/building_skill/bskill_tra_flow_gc1.webp
new file mode 100644
index 000000000..a3c6dd802
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_gc1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_gc2.webp b/ui/public/building_skill/bskill_tra_flow_gc2.webp
new file mode 100644
index 000000000..83984ba86
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_gc2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_gs.webp b/ui/public/building_skill/bskill_tra_flow_gs.webp
new file mode 100644
index 000000000..f8004dd33
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_gs.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_gs1.webp b/ui/public/building_skill/bskill_tra_flow_gs1.webp
new file mode 100644
index 000000000..b82d5c9d8
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_gs1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_flow_gs2.webp b/ui/public/building_skill/bskill_tra_flow_gs2.webp
new file mode 100644
index 000000000..2d73a28d5
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_flow_gs2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_law.webp b/ui/public/building_skill/bskill_tra_law.webp
new file mode 100644
index 000000000..cba83ed4c
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_law.webp differ
diff --git a/ui/public/building_skill/bskill_tra_limit&cost.webp b/ui/public/building_skill/bskill_tra_limit&cost.webp
new file mode 100644
index 000000000..b9f33480f
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_limit&cost.webp differ
diff --git a/ui/public/building_skill/bskill_tra_limit&trade1.webp b/ui/public/building_skill/bskill_tra_limit&trade1.webp
new file mode 100644
index 000000000..3ad9a0ff2
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_limit&trade1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_limit2spd.webp b/ui/public/building_skill/bskill_tra_limit2spd.webp
new file mode 100644
index 000000000..3deb45cd8
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_limit2spd.webp differ
diff --git a/ui/public/building_skill/bskill_tra_limit_count.webp b/ui/public/building_skill/bskill_tra_limit_count.webp
new file mode 100644
index 000000000..f567c49f2
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_limit_count.webp differ
diff --git a/ui/public/building_skill/bskill_tra_limit_diff.webp b/ui/public/building_skill/bskill_tra_limit_diff.webp
new file mode 100644
index 000000000..9056ffb4c
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_limit_diff.webp differ
diff --git a/ui/public/building_skill/bskill_tra_long1.webp b/ui/public/building_skill/bskill_tra_long1.webp
new file mode 100644
index 000000000..cccea7bd8
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_long1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_long2.webp b/ui/public/building_skill/bskill_tra_long2.webp
new file mode 100644
index 000000000..b634f4737
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_long2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_ord_spd_ext0.webp b/ui/public/building_skill/bskill_tra_ord_spd_ext0.webp
new file mode 100644
index 000000000..ec561b127
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_ord_spd_ext0.webp differ
diff --git a/ui/public/building_skill/bskill_tra_ord_spd_ext1.webp b/ui/public/building_skill/bskill_tra_ord_spd_ext1.webp
new file mode 100644
index 000000000..955a3215b
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_ord_spd_ext1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_par&per1.webp b/ui/public/building_skill/bskill_tra_par&per1.webp
new file mode 100644
index 000000000..a2a892dd6
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_par&per1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_par&per2.webp b/ui/public/building_skill/bskill_tra_par&per2.webp
new file mode 100644
index 000000000..492d258c6
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_par&per2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_par1.webp b/ui/public/building_skill/bskill_tra_par1.webp
new file mode 100644
index 000000000..8da7e7c90
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_par1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_pepe.webp b/ui/public/building_skill/bskill_tra_pepe.webp
new file mode 100644
index 000000000..766d2b03d
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_pepe.webp differ
diff --git a/ui/public/building_skill/bskill_tra_share1.webp b/ui/public/building_skill/bskill_tra_share1.webp
new file mode 100644
index 000000000..a82ce9e8e
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_share1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&cost.webp b/ui/public/building_skill/bskill_tra_spd&cost.webp
new file mode 100644
index 000000000..b8d88542e
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&cost.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&dorm1.webp b/ui/public/building_skill/bskill_tra_spd&dorm1.webp
new file mode 100644
index 000000000..ab110d9f1
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&dorm1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&dorm2.webp b/ui/public/building_skill/bskill_tra_spd&dorm2.webp
new file mode 100644
index 000000000..5632eaebb
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&dorm2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&formula1.webp b/ui/public/building_skill/bskill_tra_spd&formula1.webp
new file mode 100644
index 000000000..4ef6122ff
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&formula1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit1.webp b/ui/public/building_skill/bskill_tra_spd&limit1.webp
new file mode 100644
index 000000000..ffae9767c
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit2.webp b/ui/public/building_skill/bskill_tra_spd&limit2.webp
new file mode 100644
index 000000000..726238f81
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit3.webp b/ui/public/building_skill/bskill_tra_spd&limit3.webp
new file mode 100644
index 000000000..248532bba
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit3.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit4.webp b/ui/public/building_skill/bskill_tra_spd&limit4.webp
new file mode 100644
index 000000000..53aac19c8
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit4.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit5.webp b/ui/public/building_skill/bskill_tra_spd&limit5.webp
new file mode 100644
index 000000000..4892f679e
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit5.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit6.webp b/ui/public/building_skill/bskill_tra_spd&limit6.webp
new file mode 100644
index 000000000..ff6160f47
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit6.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit7.webp b/ui/public/building_skill/bskill_tra_spd&limit7.webp
new file mode 100644
index 000000000..e1d09d404
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit7.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit_down1.webp b/ui/public/building_skill/bskill_tra_spd&limit_down1.webp
new file mode 100644
index 000000000..f06d238a6
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit_down1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit_down2.webp b/ui/public/building_skill/bskill_tra_spd&limit_down2.webp
new file mode 100644
index 000000000..ef6d713a5
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit_down2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&limit_felyne.webp b/ui/public/building_skill/bskill_tra_spd&limit_felyne.webp
new file mode 100644
index 000000000..af03fd542
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&limit_felyne.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&meet.webp b/ui/public/building_skill/bskill_tra_spd&meet.webp
new file mode 100644
index 000000000..9242a97e9
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&meet.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&meet1.webp b/ui/public/building_skill/bskill_tra_spd&meet1.webp
new file mode 100644
index 000000000..4e5d7fd3d
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&meet1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd&wt1.webp b/ui/public/building_skill/bskill_tra_spd&wt1.webp
new file mode 100644
index 000000000..7cc933e25
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd&wt1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd1.webp b/ui/public/building_skill/bskill_tra_spd1.webp
new file mode 100644
index 000000000..aa07df8be
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd2.webp b/ui/public/building_skill/bskill_tra_spd2.webp
new file mode 100644
index 000000000..7e7c20168
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd3.webp b/ui/public/building_skill/bskill_tra_spd3.webp
new file mode 100644
index 000000000..2861ae486
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd3.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd_bd1.webp b/ui/public/building_skill/bskill_tra_spd_bd1.webp
new file mode 100644
index 000000000..83a2ec9d8
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd_bd1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd_bd2.webp b/ui/public/building_skill/bskill_tra_spd_bd2.webp
new file mode 100644
index 000000000..74bde47dc
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd_bd2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd_bd_dungeon.webp b/ui/public/building_skill/bskill_tra_spd_bd_dungeon.webp
new file mode 100644
index 000000000..c2355cc34
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd_bd_dungeon.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd_variable21.webp b/ui/public/building_skill/bskill_tra_spd_variable21.webp
new file mode 100644
index 000000000..5b5c5700e
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd_variable21.webp differ
diff --git a/ui/public/building_skill/bskill_tra_spd_variable22.webp b/ui/public/building_skill/bskill_tra_spd_variable22.webp
new file mode 100644
index 000000000..d4b8ce3a6
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_spd_variable22.webp differ
diff --git a/ui/public/building_skill/bskill_tra_texas1.webp b/ui/public/building_skill/bskill_tra_texas1.webp
new file mode 100644
index 000000000..0bfb96305
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_texas1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_texas2.webp b/ui/public/building_skill/bskill_tra_texas2.webp
new file mode 100644
index 000000000..bc23bf3db
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_texas2.webp differ
diff --git a/ui/public/building_skill/bskill_tra_vodfox.webp b/ui/public/building_skill/bskill_tra_vodfox.webp
new file mode 100644
index 000000000..251ee87eb
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_vodfox.webp differ
diff --git a/ui/public/building_skill/bskill_tra_wt&cost1.webp b/ui/public/building_skill/bskill_tra_wt&cost1.webp
new file mode 100644
index 000000000..10eeaff2c
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_wt&cost1.webp differ
diff --git a/ui/public/building_skill/bskill_tra_wt&cost2.webp b/ui/public/building_skill/bskill_tra_wt&cost2.webp
new file mode 100644
index 000000000..3d1d3d9ab
Binary files /dev/null and b/ui/public/building_skill/bskill_tra_wt&cost2.webp differ
diff --git a/ui/public/building_skill/bskill_trade_ord_spd_variable.webp b/ui/public/building_skill/bskill_trade_ord_spd_variable.webp
new file mode 100644
index 000000000..614f2d9d1
Binary files /dev/null and b/ui/public/building_skill/bskill_trade_ord_spd_variable.webp differ
diff --git a/ui/public/building_skill/bskill_train1_caster1.webp b/ui/public/building_skill/bskill_train1_caster1.webp
new file mode 100644
index 000000000..38fa750dd
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_caster1.webp differ
diff --git a/ui/public/building_skill/bskill_train1_defender1.webp b/ui/public/building_skill/bskill_train1_defender1.webp
new file mode 100644
index 000000000..16a83cbb3
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_defender1.webp differ
diff --git a/ui/public/building_skill/bskill_train1_guard1.webp b/ui/public/building_skill/bskill_train1_guard1.webp
new file mode 100644
index 000000000..2480df7e6
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_guard1.webp differ
diff --git a/ui/public/building_skill/bskill_train1_sniper2.webp b/ui/public/building_skill/bskill_train1_sniper2.webp
new file mode 100644
index 000000000..7550e2664
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_sniper2.webp differ
diff --git a/ui/public/building_skill/bskill_train1_specialist1.webp b/ui/public/building_skill/bskill_train1_specialist1.webp
new file mode 100644
index 000000000..44109e22a
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_specialist1.webp differ
diff --git a/ui/public/building_skill/bskill_train1_vanguard1.webp b/ui/public/building_skill/bskill_train1_vanguard1.webp
new file mode 100644
index 000000000..84143f8a5
Binary files /dev/null and b/ui/public/building_skill/bskill_train1_vanguard1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_caster1.webp b/ui/public/building_skill/bskill_train2_caster1.webp
new file mode 100644
index 000000000..dad12da23
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_caster1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_defender1.webp b/ui/public/building_skill/bskill_train2_defender1.webp
new file mode 100644
index 000000000..406671bab
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_defender1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_guard1.webp b/ui/public/building_skill/bskill_train2_guard1.webp
new file mode 100644
index 000000000..ddf32cb9c
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_guard1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_medic1.webp b/ui/public/building_skill/bskill_train2_medic1.webp
new file mode 100644
index 000000000..8bcf94ae2
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_medic1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_sniper1.webp b/ui/public/building_skill/bskill_train2_sniper1.webp
new file mode 100644
index 000000000..0bb7ec10f
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_sniper1.webp differ
diff --git a/ui/public/building_skill/bskill_train2_specialist&pioneer1.webp b/ui/public/building_skill/bskill_train2_specialist&pioneer1.webp
new file mode 100644
index 000000000..0abe762b9
Binary files /dev/null and b/ui/public/building_skill/bskill_train2_specialist&pioneer1.webp differ
diff --git a/ui/public/building_skill/bskill_train3_guard1.webp b/ui/public/building_skill/bskill_train3_guard1.webp
new file mode 100644
index 000000000..1484efad4
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_guard1.webp differ
diff --git a/ui/public/building_skill/bskill_train3_guard2.webp b/ui/public/building_skill/bskill_train3_guard2.webp
new file mode 100644
index 000000000..b91ab14e5
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_guard2.webp differ
diff --git a/ui/public/building_skill/bskill_train3_sniper1.webp b/ui/public/building_skill/bskill_train3_sniper1.webp
new file mode 100644
index 000000000..5bed41f19
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_sniper1.webp differ
diff --git a/ui/public/building_skill/bskill_train3_sniper2.webp b/ui/public/building_skill/bskill_train3_sniper2.webp
new file mode 100644
index 000000000..d5d4cf671
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_sniper2.webp differ
diff --git a/ui/public/building_skill/bskill_train3_sniper2_w.webp b/ui/public/building_skill/bskill_train3_sniper2_w.webp
new file mode 100644
index 000000000..fd5ef03ae
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_sniper2_w.webp differ
diff --git a/ui/public/building_skill/bskill_train3_specialist2.webp b/ui/public/building_skill/bskill_train3_specialist2.webp
new file mode 100644
index 000000000..79f615421
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_specialist2.webp differ
diff --git a/ui/public/building_skill/bskill_train3_supporter2.webp b/ui/public/building_skill/bskill_train3_supporter2.webp
new file mode 100644
index 000000000..7fa19345d
Binary files /dev/null and b/ui/public/building_skill/bskill_train3_supporter2.webp differ
diff --git a/ui/public/building_skill/bskill_train_abyssal.webp b/ui/public/building_skill/bskill_train_abyssal.webp
new file mode 100644
index 000000000..c81591108
Binary files /dev/null and b/ui/public/building_skill/bskill_train_abyssal.webp differ
diff --git a/ui/public/building_skill/bskill_train_accmu_guard1.webp b/ui/public/building_skill/bskill_train_accmu_guard1.webp
new file mode 100644
index 000000000..975d7c468
Binary files /dev/null and b/ui/public/building_skill/bskill_train_accmu_guard1.webp differ
diff --git a/ui/public/building_skill/bskill_train_all.webp b/ui/public/building_skill/bskill_train_all.webp
new file mode 100644
index 000000000..e2c2b4a22
Binary files /dev/null and b/ui/public/building_skill/bskill_train_all.webp differ
diff --git a/ui/public/building_skill/bskill_train_artsprotector.webp b/ui/public/building_skill/bskill_train_artsprotector.webp
new file mode 100644
index 000000000..e6c769b8f
Binary files /dev/null and b/ui/public/building_skill/bskill_train_artsprotector.webp differ
diff --git a/ui/public/building_skill/bskill_train_attack.webp b/ui/public/building_skill/bskill_train_attack.webp
new file mode 100644
index 000000000..22294a5dd
Binary files /dev/null and b/ui/public/building_skill/bskill_train_attack.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster&medic1.webp b/ui/public/building_skill/bskill_train_caster&medic1.webp
new file mode 100644
index 000000000..76b8af1bc
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster&medic1.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster&medic2.webp b/ui/public/building_skill/bskill_train_caster&medic2.webp
new file mode 100644
index 000000000..137fcc312
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster&medic2.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster&supporter1.webp b/ui/public/building_skill/bskill_train_caster&supporter1.webp
new file mode 100644
index 000000000..36c5024cd
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster&supporter1.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster1.webp b/ui/public/building_skill/bskill_train_caster1.webp
new file mode 100644
index 000000000..7fde63ecb
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster1.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster2.webp b/ui/public/building_skill/bskill_train_caster2.webp
new file mode 100644
index 000000000..8f16d4803
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster2.webp differ
diff --git a/ui/public/building_skill/bskill_train_caster3.webp b/ui/public/building_skill/bskill_train_caster3.webp
new file mode 100644
index 000000000..70df2aa20
Binary files /dev/null and b/ui/public/building_skill/bskill_train_caster3.webp differ
diff --git a/ui/public/building_skill/bskill_train_chainhealer.webp b/ui/public/building_skill/bskill_train_chainhealer.webp
new file mode 100644
index 000000000..1157022ba
Binary files /dev/null and b/ui/public/building_skill/bskill_train_chainhealer.webp differ
diff --git a/ui/public/building_skill/bskill_train_chen.webp b/ui/public/building_skill/bskill_train_chen.webp
new file mode 100644
index 000000000..c7668682c
Binary files /dev/null and b/ui/public/building_skill/bskill_train_chen.webp differ
diff --git a/ui/public/building_skill/bskill_train_defender1.webp b/ui/public/building_skill/bskill_train_defender1.webp
new file mode 100644
index 000000000..10348db66
Binary files /dev/null and b/ui/public/building_skill/bskill_train_defender1.webp differ
diff --git a/ui/public/building_skill/bskill_train_defender2.webp b/ui/public/building_skill/bskill_train_defender2.webp
new file mode 100644
index 000000000..9a2cbb99d
Binary files /dev/null and b/ui/public/building_skill/bskill_train_defender2.webp differ
diff --git a/ui/public/building_skill/bskill_train_defender3.webp b/ui/public/building_skill/bskill_train_defender3.webp
new file mode 100644
index 000000000..43175f54c
Binary files /dev/null and b/ui/public/building_skill/bskill_train_defender3.webp differ
diff --git a/ui/public/building_skill/bskill_train_defense.webp b/ui/public/building_skill/bskill_train_defense.webp
new file mode 100644
index 000000000..044f46a16
Binary files /dev/null and b/ui/public/building_skill/bskill_train_defense.webp differ
diff --git a/ui/public/building_skill/bskill_train_fastshot.webp b/ui/public/building_skill/bskill_train_fastshot.webp
new file mode 100644
index 000000000..75bcd2ca9
Binary files /dev/null and b/ui/public/building_skill/bskill_train_fastshot.webp differ
diff --git a/ui/public/building_skill/bskill_train_fighter.webp b/ui/public/building_skill/bskill_train_fighter.webp
new file mode 100644
index 000000000..74fe986bc
Binary files /dev/null and b/ui/public/building_skill/bskill_train_fighter.webp differ
diff --git a/ui/public/building_skill/bskill_train_gavial.webp b/ui/public/building_skill/bskill_train_gavial.webp
new file mode 100644
index 000000000..36fc45845
Binary files /dev/null and b/ui/public/building_skill/bskill_train_gavial.webp differ
diff --git a/ui/public/building_skill/bskill_train_guard&supporter.webp b/ui/public/building_skill/bskill_train_guard&supporter.webp
new file mode 100644
index 000000000..aa68631cb
Binary files /dev/null and b/ui/public/building_skill/bskill_train_guard&supporter.webp differ
diff --git a/ui/public/building_skill/bskill_train_guard1.webp b/ui/public/building_skill/bskill_train_guard1.webp
new file mode 100644
index 000000000..0e5f627b2
Binary files /dev/null and b/ui/public/building_skill/bskill_train_guard1.webp differ
diff --git a/ui/public/building_skill/bskill_train_guard2.webp b/ui/public/building_skill/bskill_train_guard2.webp
new file mode 100644
index 000000000..4c2a9e375
Binary files /dev/null and b/ui/public/building_skill/bskill_train_guard2.webp differ
diff --git a/ui/public/building_skill/bskill_train_guard3.webp b/ui/public/building_skill/bskill_train_guard3.webp
new file mode 100644
index 000000000..0221c6cad
Binary files /dev/null and b/ui/public/building_skill/bskill_train_guard3.webp differ
diff --git a/ui/public/building_skill/bskill_train_knight_bd1.webp b/ui/public/building_skill/bskill_train_knight_bd1.webp
new file mode 100644
index 000000000..ad1427251
Binary files /dev/null and b/ui/public/building_skill/bskill_train_knight_bd1.webp differ
diff --git a/ui/public/building_skill/bskill_train_knight_bd2.webp b/ui/public/building_skill/bskill_train_knight_bd2.webp
new file mode 100644
index 000000000..25c063875
Binary files /dev/null and b/ui/public/building_skill/bskill_train_knight_bd2.webp differ
diff --git a/ui/public/building_skill/bskill_train_lord.webp b/ui/public/building_skill/bskill_train_lord.webp
new file mode 100644
index 000000000..4a40b9e7f
Binary files /dev/null and b/ui/public/building_skill/bskill_train_lord.webp differ
diff --git a/ui/public/building_skill/bskill_train_medic1.webp b/ui/public/building_skill/bskill_train_medic1.webp
new file mode 100644
index 000000000..80be73702
Binary files /dev/null and b/ui/public/building_skill/bskill_train_medic1.webp differ
diff --git a/ui/public/building_skill/bskill_train_medic2.webp b/ui/public/building_skill/bskill_train_medic2.webp
new file mode 100644
index 000000000..3ceb8f6f7
Binary files /dev/null and b/ui/public/building_skill/bskill_train_medic2.webp differ
diff --git a/ui/public/building_skill/bskill_train_medic3.webp b/ui/public/building_skill/bskill_train_medic3.webp
new file mode 100644
index 000000000..1dd415b18
Binary files /dev/null and b/ui/public/building_skill/bskill_train_medic3.webp differ
diff --git a/ui/public/building_skill/bskill_train_reduceTime.webp b/ui/public/building_skill/bskill_train_reduceTime.webp
new file mode 100644
index 000000000..84ae92c4a
Binary files /dev/null and b/ui/public/building_skill/bskill_train_reduceTime.webp differ
diff --git a/ui/public/building_skill/bskill_train_sami.webp b/ui/public/building_skill/bskill_train_sami.webp
new file mode 100644
index 000000000..e37bf19a4
Binary files /dev/null and b/ui/public/building_skill/bskill_train_sami.webp differ
diff --git a/ui/public/building_skill/bskill_train_siegesniper.webp b/ui/public/building_skill/bskill_train_siegesniper.webp
new file mode 100644
index 000000000..2d7787939
Binary files /dev/null and b/ui/public/building_skill/bskill_train_siegesniper.webp differ
diff --git a/ui/public/building_skill/bskill_train_skadi.webp b/ui/public/building_skill/bskill_train_skadi.webp
new file mode 100644
index 000000000..e65ea59c5
Binary files /dev/null and b/ui/public/building_skill/bskill_train_skadi.webp differ
diff --git a/ui/public/building_skill/bskill_train_sniper1.webp b/ui/public/building_skill/bskill_train_sniper1.webp
new file mode 100644
index 000000000..cafa81bb0
Binary files /dev/null and b/ui/public/building_skill/bskill_train_sniper1.webp differ
diff --git a/ui/public/building_skill/bskill_train_sniper2.webp b/ui/public/building_skill/bskill_train_sniper2.webp
new file mode 100644
index 000000000..e94e67302
Binary files /dev/null and b/ui/public/building_skill/bskill_train_sniper2.webp differ
diff --git a/ui/public/building_skill/bskill_train_sniper3.webp b/ui/public/building_skill/bskill_train_sniper3.webp
new file mode 100644
index 000000000..26db78b9c
Binary files /dev/null and b/ui/public/building_skill/bskill_train_sniper3.webp differ
diff --git a/ui/public/building_skill/bskill_train_specialist&pioneer1.webp b/ui/public/building_skill/bskill_train_specialist&pioneer1.webp
new file mode 100644
index 000000000..3cb7c6537
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specialist&pioneer1.webp differ
diff --git a/ui/public/building_skill/bskill_train_specialist1.webp b/ui/public/building_skill/bskill_train_specialist1.webp
new file mode 100644
index 000000000..9cea9e035
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specialist1.webp differ
diff --git a/ui/public/building_skill/bskill_train_specialist2.webp b/ui/public/building_skill/bskill_train_specialist2.webp
new file mode 100644
index 000000000..4c59dc225
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specialist2.webp differ
diff --git a/ui/public/building_skill/bskill_train_specialist3.webp b/ui/public/building_skill/bskill_train_specialist3.webp
new file mode 100644
index 000000000..f3bb33c97
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specialist3.webp differ
diff --git a/ui/public/building_skill/bskill_train_specialist4.webp b/ui/public/building_skill/bskill_train_specialist4.webp
new file mode 100644
index 000000000..2b2f86bd0
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specialist4.webp differ
diff --git a/ui/public/building_skill/bskill_train_specter.webp b/ui/public/building_skill/bskill_train_specter.webp
new file mode 100644
index 000000000..13000928c
Binary files /dev/null and b/ui/public/building_skill/bskill_train_specter.webp differ
diff --git a/ui/public/building_skill/bskill_train_supporter1.webp b/ui/public/building_skill/bskill_train_supporter1.webp
new file mode 100644
index 000000000..aca3a35e1
Binary files /dev/null and b/ui/public/building_skill/bskill_train_supporter1.webp differ
diff --git a/ui/public/building_skill/bskill_train_supporter2.webp b/ui/public/building_skill/bskill_train_supporter2.webp
new file mode 100644
index 000000000..eefa82e4b
Binary files /dev/null and b/ui/public/building_skill/bskill_train_supporter2.webp differ
diff --git a/ui/public/building_skill/bskill_train_supporter3.webp b/ui/public/building_skill/bskill_train_supporter3.webp
new file mode 100644
index 000000000..c74feb40d
Binary files /dev/null and b/ui/public/building_skill/bskill_train_supporter3.webp differ
diff --git a/ui/public/building_skill/bskill_train_sword.webp b/ui/public/building_skill/bskill_train_sword.webp
new file mode 100644
index 000000000..24e38d8ae
Binary files /dev/null and b/ui/public/building_skill/bskill_train_sword.webp differ
diff --git a/ui/public/building_skill/bskill_train_vanguard&sniper.webp b/ui/public/building_skill/bskill_train_vanguard&sniper.webp
new file mode 100644
index 000000000..8229414e2
Binary files /dev/null and b/ui/public/building_skill/bskill_train_vanguard&sniper.webp differ
diff --git a/ui/public/building_skill/bskill_train_vanguard1.webp b/ui/public/building_skill/bskill_train_vanguard1.webp
new file mode 100644
index 000000000..d8d0d355d
Binary files /dev/null and b/ui/public/building_skill/bskill_train_vanguard1.webp differ
diff --git a/ui/public/building_skill/bskill_train_vanguard2.webp b/ui/public/building_skill/bskill_train_vanguard2.webp
new file mode 100644
index 000000000..9f37720a7
Binary files /dev/null and b/ui/public/building_skill/bskill_train_vanguard2.webp differ
diff --git a/ui/public/building_skill/bskill_train_vanguard3.webp b/ui/public/building_skill/bskill_train_vanguard3.webp
new file mode 100644
index 000000000..2f2573712
Binary files /dev/null and b/ui/public/building_skill/bskill_train_vanguard3.webp differ
diff --git a/ui/public/building_skill/bskill_train_w.webp b/ui/public/building_skill/bskill_train_w.webp
new file mode 100644
index 000000000..a8966c4d8
Binary files /dev/null and b/ui/public/building_skill/bskill_train_w.webp differ
diff --git a/ui/public/building_skill/bskill_train_wandermedic.webp b/ui/public/building_skill/bskill_train_wandermedic.webp
new file mode 100644
index 000000000..117eac766
Binary files /dev/null and b/ui/public/building_skill/bskill_train_wandermedic.webp differ
diff --git a/ui/public/building_skill/bskill_ws_Ketone.webp b/ui/public/building_skill/bskill_ws_Ketone.webp
new file mode 100644
index 000000000..df1d07bda
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_Ketone.webp differ
diff --git a/ui/public/building_skill/bskill_ws_aegir.webp b/ui/public/building_skill/bskill_ws_aegir.webp
new file mode 100644
index 000000000..4c703bfb2
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_aegir.webp differ
diff --git a/ui/public/building_skill/bskill_ws_aegir2.webp b/ui/public/building_skill/bskill_ws_aegir2.webp
new file mode 100644
index 000000000..d72859c15
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_aegir2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_all_cost1.webp b/ui/public/building_skill/bskill_ws_all_cost1.webp
new file mode 100644
index 000000000..cbef17304
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_all_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_alloyBlock.webp b/ui/public/building_skill/bskill_ws_alloyBlock.webp
new file mode 100644
index 000000000..28b8990ce
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_alloyBlock.webp differ
diff --git a/ui/public/building_skill/bskill_ws_asc1.webp b/ui/public/building_skill/bskill_ws_asc1.webp
new file mode 100644
index 000000000..2c243dcbb
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_asc1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_asc2.webp b/ui/public/building_skill/bskill_ws_asc2.webp
new file mode 100644
index 000000000..55f61ada8
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_asc2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_asc_cost1.webp b/ui/public/building_skill/bskill_ws_asc_cost1.webp
new file mode 100644
index 000000000..77bddd187
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_asc_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_bonus1.webp b/ui/public/building_skill/bskill_ws_bonus1.webp
new file mode 100644
index 000000000..5e9a4b6fe
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_bonus1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_bonus2.webp b/ui/public/building_skill/bskill_ws_bonus2.webp
new file mode 100644
index 000000000..d78b7905c
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_bonus2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build1.webp b/ui/public/building_skill/bskill_ws_build1.webp
new file mode 100644
index 000000000..6574bf2cf
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build2.webp b/ui/public/building_skill/bskill_ws_build2.webp
new file mode 100644
index 000000000..a5f4c5abc
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build3.webp b/ui/public/building_skill/bskill_ws_build3.webp
new file mode 100644
index 000000000..e1b4d48a0
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build4.webp b/ui/public/building_skill/bskill_ws_build4.webp
new file mode 100644
index 000000000..591248b14
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build4.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build5.webp b/ui/public/building_skill/bskill_ws_build5.webp
new file mode 100644
index 000000000..dabaad204
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build5.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build_cost.webp b/ui/public/building_skill/bskill_ws_build_cost.webp
new file mode 100644
index 000000000..08df0a65d
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build_cost.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build_cost1.webp b/ui/public/building_skill/bskill_ws_build_cost1.webp
new file mode 100644
index 000000000..042f7ca3e
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_build_cost2.webp b/ui/public/building_skill/bskill_ws_build_cost2.webp
new file mode 100644
index 000000000..2149e11e9
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_build_cost2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_constant.webp b/ui/public/building_skill/bskill_ws_constant.webp
new file mode 100644
index 000000000..5865aac43
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_constant.webp differ
diff --git a/ui/public/building_skill/bskill_ws_constant2.webp b/ui/public/building_skill/bskill_ws_constant2.webp
new file mode 100644
index 000000000..385b000e4
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_constant2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_constant3.webp b/ui/public/building_skill/bskill_ws_constant3.webp
new file mode 100644
index 000000000..8ac75aab0
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_constant3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_cost&dorm.webp b/ui/public/building_skill/bskill_ws_cost&dorm.webp
new file mode 100644
index 000000000..3486d5824
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_cost&dorm.webp differ
diff --git a/ui/public/building_skill/bskill_ws_cost3.webp b/ui/public/building_skill/bskill_ws_cost3.webp
new file mode 100644
index 000000000..a726ef6ea
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_cost3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_cost_blemishine.webp b/ui/public/building_skill/bskill_ws_cost_blemishine.webp
new file mode 100644
index 000000000..0be5904a5
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_cost_blemishine.webp differ
diff --git a/ui/public/building_skill/bskill_ws_cost_lolxh.webp b/ui/public/building_skill/bskill_ws_cost_lolxh.webp
new file mode 100644
index 000000000..eb114505f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_cost_lolxh.webp differ
diff --git a/ui/public/building_skill/bskill_ws_cost_magallan.webp b/ui/public/building_skill/bskill_ws_cost_magallan.webp
new file mode 100644
index 000000000..a373fd541
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_cost_magallan.webp differ
diff --git a/ui/public/building_skill/bskill_ws_device.webp b/ui/public/building_skill/bskill_ws_device.webp
new file mode 100644
index 000000000..3f2d8d18f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_device.webp differ
diff --git a/ui/public/building_skill/bskill_ws_drop_orirock.webp b/ui/public/building_skill/bskill_ws_drop_orirock.webp
new file mode 100644
index 000000000..b75bba217
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_drop_orirock.webp differ
diff --git a/ui/public/building_skill/bskill_ws_drop_oriron.webp b/ui/public/building_skill/bskill_ws_drop_oriron.webp
new file mode 100644
index 000000000..35be7a248
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_drop_oriron.webp differ
diff --git a/ui/public/building_skill/bskill_ws_drop_polyester.webp b/ui/public/building_skill/bskill_ws_drop_polyester.webp
new file mode 100644
index 000000000..0b9b3fcbf
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_drop_polyester.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve1.webp b/ui/public/building_skill/bskill_ws_evolve1.webp
new file mode 100644
index 000000000..16e05b722
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve2.webp b/ui/public/building_skill/bskill_ws_evolve2.webp
new file mode 100644
index 000000000..95667124b
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve3.webp b/ui/public/building_skill/bskill_ws_evolve3.webp
new file mode 100644
index 000000000..b50a5e48f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve4.webp b/ui/public/building_skill/bskill_ws_evolve4.webp
new file mode 100644
index 000000000..35492ebdb
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve4.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_cost.webp b/ui/public/building_skill/bskill_ws_evolve_cost.webp
new file mode 100644
index 000000000..549ae00ff
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_cost.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_cost1.webp b/ui/public/building_skill/bskill_ws_evolve_cost1.webp
new file mode 100644
index 000000000..87f607395
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_cost1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_cost2.webp b/ui/public/building_skill/bskill_ws_evolve_cost2.webp
new file mode 100644
index 000000000..60628175c
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_cost2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_cost3.webp b/ui/public/building_skill/bskill_ws_evolve_cost3.webp
new file mode 100644
index 000000000..6867caafd
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_cost3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_dorm1.webp b/ui/public/building_skill/bskill_ws_evolve_dorm1.webp
new file mode 100644
index 000000000..c174ede5a
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_dorm1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_dorm2.webp b/ui/public/building_skill/bskill_ws_evolve_dorm2.webp
new file mode 100644
index 000000000..464399078
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_dorm2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_evolve_dorm3.webp b/ui/public/building_skill/bskill_ws_evolve_dorm3.webp
new file mode 100644
index 000000000..4f95cec24
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_evolve_dorm3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_free.webp b/ui/public/building_skill/bskill_ws_free.webp
new file mode 100644
index 000000000..349ab094c
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_free.webp differ
diff --git a/ui/public/building_skill/bskill_ws_frost.webp b/ui/public/building_skill/bskill_ws_frost.webp
new file mode 100644
index 000000000..5d3d9ed89
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_frost.webp differ
diff --git a/ui/public/building_skill/bskill_ws_lolxh.webp b/ui/public/building_skill/bskill_ws_lolxh.webp
new file mode 100644
index 000000000..7ee7670f2
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_lolxh.webp differ
diff --git a/ui/public/building_skill/bskill_ws_nian.webp b/ui/public/building_skill/bskill_ws_nian.webp
new file mode 100644
index 000000000..83a3fc81e
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_nian.webp differ
diff --git a/ui/public/building_skill/bskill_ws_orirock.webp b/ui/public/building_skill/bskill_ws_orirock.webp
new file mode 100644
index 000000000..4e778997f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_orirock.webp differ
diff --git a/ui/public/building_skill/bskill_ws_oriron.webp b/ui/public/building_skill/bskill_ws_oriron.webp
new file mode 100644
index 000000000..484bd5429
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_oriron.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p1.webp b/ui/public/building_skill/bskill_ws_p1.webp
new file mode 100644
index 000000000..a58f70e7f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p2.webp b/ui/public/building_skill/bskill_ws_p2.webp
new file mode 100644
index 000000000..9943d110a
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p3.webp b/ui/public/building_skill/bskill_ws_p3.webp
new file mode 100644
index 000000000..6ba08ca87
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p4.webp b/ui/public/building_skill/bskill_ws_p4.webp
new file mode 100644
index 000000000..f0857b1d9
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p4.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p5.webp b/ui/public/building_skill/bskill_ws_p5.webp
new file mode 100644
index 000000000..0dd00884a
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p5.webp differ
diff --git a/ui/public/building_skill/bskill_ws_p6.webp b/ui/public/building_skill/bskill_ws_p6.webp
new file mode 100644
index 000000000..0dd00884a
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_p6.webp differ
diff --git a/ui/public/building_skill/bskill_ws_polyester.webp b/ui/public/building_skill/bskill_ws_polyester.webp
new file mode 100644
index 000000000..2d899b024
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_polyester.webp differ
diff --git a/ui/public/building_skill/bskill_ws_recovery.webp b/ui/public/building_skill/bskill_ws_recovery.webp
new file mode 100644
index 000000000..771b6f63f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_recovery.webp differ
diff --git a/ui/public/building_skill/bskill_ws_rub.webp b/ui/public/building_skill/bskill_ws_rub.webp
new file mode 100644
index 000000000..5453a17df
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_rub.webp differ
diff --git a/ui/public/building_skill/bskill_ws_skill1.webp b/ui/public/building_skill/bskill_ws_skill1.webp
new file mode 100644
index 000000000..4edc59dd1
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_skill1.webp differ
diff --git a/ui/public/building_skill/bskill_ws_skill2.webp b/ui/public/building_skill/bskill_ws_skill2.webp
new file mode 100644
index 000000000..49bfccd3f
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_skill2.webp differ
diff --git a/ui/public/building_skill/bskill_ws_skill3.webp b/ui/public/building_skill/bskill_ws_skill3.webp
new file mode 100644
index 000000000..e37e01417
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_skill3.webp differ
diff --git a/ui/public/building_skill/bskill_ws_skill_cost1.webp b/ui/public/building_skill/bskill_ws_skill_cost1.webp
new file mode 100644
index 000000000..cf12b5b94
Binary files /dev/null and b/ui/public/building_skill/bskill_ws_skill_cost1.webp differ
diff --git a/ui/public/building_skill/trade_ord_flow_gold1.webp b/ui/public/building_skill/trade_ord_flow_gold1.webp
new file mode 100644
index 000000000..3f5772e94
Binary files /dev/null and b/ui/public/building_skill/trade_ord_flow_gold1.webp differ
diff --git a/ui/public/building_skill/trade_ord_flow_gold2.webp b/ui/public/building_skill/trade_ord_flow_gold2.webp
new file mode 100644
index 000000000..f2370a953
Binary files /dev/null and b/ui/public/building_skill/trade_ord_flow_gold2.webp differ
diff --git a/ui/public/building_skill/trade_ord_spd&gold1.webp b/ui/public/building_skill/trade_ord_spd&gold1.webp
new file mode 100644
index 000000000..262e6cac1
Binary files /dev/null and b/ui/public/building_skill/trade_ord_spd&gold1.webp differ
diff --git a/ui/public/building_skill/trade_ord_spd&gold2.webp b/ui/public/building_skill/trade_ord_spd&gold2.webp
new file mode 100644
index 000000000..240db4779
Binary files /dev/null and b/ui/public/building_skill/trade_ord_spd&gold2.webp differ
diff --git "a/ui/public/depot/12F\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/12F\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cc44a7597
Binary files /dev/null and "b/ui/public/depot/12F\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/32h\346\210\230\347\225\245\351\205\215\347\273\231.webp" "b/ui/public/depot/32h\346\210\230\347\225\245\351\205\215\347\273\231.webp"
new file mode 100644
index 000000000..f750405e8
Binary files /dev/null and "b/ui/public/depot/32h\346\210\230\347\225\245\351\205\215\347\273\231.webp" differ
diff --git "a/ui/public/depot/Castle-3\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/Castle-3\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..aab679e3a
Binary files /dev/null and "b/ui/public/depot/Castle-3\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/D32\351\222\242.webp" "b/ui/public/depot/D32\351\222\242.webp"
new file mode 100644
index 000000000..37a62af14
Binary files /dev/null and "b/ui/public/depot/D32\351\222\242.webp" differ
diff --git a/ui/public/depot/EXP.webp b/ui/public/depot/EXP.webp
new file mode 100644
index 000000000..81cfbfacb
Binary files /dev/null and b/ui/public/depot/EXP.webp differ
diff --git "a/ui/public/depot/Friston-3\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/Friston-3\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..89600d39b
Binary files /dev/null and "b/ui/public/depot/Friston-3\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/ID\344\277\241\346\201\257\346\233\264\346\226\260\345\215\241.webp" "b/ui/public/depot/ID\344\277\241\346\201\257\346\233\264\346\226\260\345\215\241.webp"
new file mode 100644
index 000000000..a34b536c5
Binary files /dev/null and "b/ui/public/depot/ID\344\277\241\346\201\257\346\233\264\346\226\260\345\215\241.webp" differ
diff --git "a/ui/public/depot/Lancet-2\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/Lancet-2\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..949e92abc
Binary files /dev/null and "b/ui/public/depot/Lancet-2\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/PRTS\345\211\277\347\201\255\344\273\243\347\220\206\345\215\241.webp" "b/ui/public/depot/PRTS\345\211\277\347\201\255\344\273\243\347\220\206\345\215\241.webp"
new file mode 100644
index 000000000..d52d86ed0
Binary files /dev/null and "b/ui/public/depot/PRTS\345\211\277\347\201\255\344\273\243\347\220\206\345\215\241.webp" differ
diff --git "a/ui/public/depot/PhonoR-0\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/PhonoR-0\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..413bb5f7e
Binary files /dev/null and "b/ui/public/depot/PhonoR-0\347\232\204\344\277\241\347\211\251.webp" differ
diff --git a/ui/public/depot/RMA70-12.webp b/ui/public/depot/RMA70-12.webp
new file mode 100644
index 000000000..f8bd86cf0
Binary files /dev/null and b/ui/public/depot/RMA70-12.webp differ
diff --git a/ui/public/depot/RMA70-24.webp b/ui/public/depot/RMA70-24.webp
new file mode 100644
index 000000000..19a33af18
Binary files /dev/null and b/ui/public/depot/RMA70-24.webp differ
diff --git "a/ui/public/depot/THRM-EX\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/THRM-EX\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4c6a7bd0b
Binary files /dev/null and "b/ui/public/depot/THRM-EX\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/W\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/W\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..026ad1093
Binary files /dev/null and "b/ui/public/depot/W\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\270\207\351\241\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\270\207\351\241\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..20f52b540
Binary files /dev/null and "b/ui/public/depot/\344\270\207\351\241\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\270\211\346\260\264\351\224\260\347\237\277.webp" "b/ui/public/depot/\344\270\211\346\260\264\351\224\260\347\237\277.webp"
new file mode 100644
index 000000000..fa77c523b
Binary files /dev/null and "b/ui/public/depot/\344\270\211\346\260\264\351\224\260\347\237\277.webp" differ
diff --git "a/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" "b/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp"
new file mode 100644
index 000000000..ac5fcc90d
Binary files /dev/null and "b/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" differ
diff --git "a/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" "b/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp"
new file mode 100644
index 000000000..45d2084c4
Binary files /dev/null and "b/ui/public/depot/\344\270\223\344\270\232\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" differ
diff --git "a/ui/public/depot/\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..2500627c7
Binary files /dev/null and "b/ui/public/depot/\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\344\270\255\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" "b/ui/public/depot/\344\270\255\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp"
new file mode 100644
index 000000000..e01090533
Binary files /dev/null and "b/ui/public/depot/\344\270\255\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" differ
diff --git "a/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..faa9fea1b
Binary files /dev/null and "b/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3abdf06bc
Binary files /dev/null and "b/ui/public/depot/\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\271\214\345\260\224\346\257\224\345\256\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\271\214\345\260\224\346\257\224\345\256\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..88ddb6a95
Binary files /dev/null and "b/ui/public/depot/\344\271\214\345\260\224\346\257\224\345\256\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..80ec38254
Binary files /dev/null and "b/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..149ca51ee
Binary files /dev/null and "b/ui/public/depot/\344\271\214\346\234\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\272\213\347\233\270\347\242\216\347\211\207.webp" "b/ui/public/depot/\344\272\213\347\233\270\347\242\216\347\211\207.webp"
new file mode 100644
index 000000000..33d5cf14c
Binary files /dev/null and "b/ui/public/depot/\344\272\213\347\233\270\347\242\216\347\211\207.webp" differ
diff --git "a/ui/public/depot/\344\272\224\346\260\264\347\240\224\347\243\250\347\237\263.webp" "b/ui/public/depot/\344\272\224\346\260\264\347\240\224\347\243\250\347\237\263.webp"
new file mode 100644
index 000000000..f9cf67194
Binary files /dev/null and "b/ui/public/depot/\344\272\224\346\260\264\347\240\224\347\243\250\347\237\263.webp" differ
diff --git "a/ui/public/depot/\344\272\232\345\217\266\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\272\232\345\217\266\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c8b71c121
Binary files /dev/null and "b/ui/public/depot/\344\272\232\345\217\266\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\273\207\347\231\275\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\273\207\347\231\275\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..12623f5f4
Binary files /dev/null and "b/ui/public/depot/\344\273\207\347\231\275\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\273\243\347\263\226.webp" "b/ui/public/depot/\344\273\243\347\263\226.webp"
new file mode 100644
index 000000000..6e40876c6
Binary files /dev/null and "b/ui/public/depot/\344\273\243\347\263\226.webp" differ
diff --git "a/ui/public/depot/\344\273\244\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\273\244\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..75eb6a71f
Binary files /dev/null and "b/ui/public/depot/\344\273\244\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\212\345\206\205\344\270\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\212\345\206\205\344\270\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c23173942
Binary files /dev/null and "b/ui/public/depot/\344\274\212\345\206\205\344\270\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\212\346\241\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\212\346\241\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f419a0b1b
Binary files /dev/null and "b/ui/public/depot/\344\274\212\346\241\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5771f7dcd
Binary files /dev/null and "b/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..aee17eb7d
Binary files /dev/null and "b/ui/public/depot/\344\274\212\350\212\231\345\210\251\347\211\271\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2e257116c
Binary files /dev/null and "b/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8f5de474f
Binary files /dev/null and "b/ui/public/depot/\344\274\221\350\260\237\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\275\206\344\271\246\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\275\206\344\271\246\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0a14b6aea
Binary files /dev/null and "b/ui/public/depot/\344\275\206\344\271\246\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\344\275\251\344\275\251\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\344\275\251\344\275\251\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6b794fb5d
Binary files /dev/null and "b/ui/public/depot/\344\275\251\344\275\251\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..737187db3
Binary files /dev/null and "b/ui/public/depot/\345\201\207\346\227\245\345\250\201\351\276\231\351\231\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9241f993a
Binary files /dev/null and "b/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e2f091e91
Binary files /dev/null and "b/ui/public/depot/\345\202\200\345\275\261\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\205\210\351\224\213\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0af82f4c5
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..7a902aa31
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..835914fc7
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..a9660db82
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\345\205\210\351\224\213\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..8f98ab637
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\205\210\351\224\213\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d274473cc
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207.webp" "b/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..b9ba0b90b
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..8ad229c8c
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\345\205\210\351\224\213\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\205\210\351\224\213\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ee1bc9d1e
Binary files /dev/null and "b/ui/public/depot/\345\205\210\351\224\213\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\211\350\260\261\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\345\205\211\350\260\261\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..5173eece1
Binary files /dev/null and "b/ui/public/depot/\345\205\211\350\260\261\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0315ed303
Binary files /dev/null and "b/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..902d99f80
Binary files /dev/null and "b/ui/public/depot/\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\205\250\346\226\260\350\243\205\347\275\256.webp" "b/ui/public/depot/\345\205\250\346\226\260\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..b9edf8c9b
Binary files /dev/null and "b/ui/public/depot/\345\205\250\346\226\260\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2053\345\205\221\346\215\242\345\210\270\302\267I.webp" "b/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2053\345\205\221\346\215\242\345\210\270\302\267I.webp"
new file mode 100644
index 000000000..29bc5b347
Binary files /dev/null and "b/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2053\345\205\221\346\215\242\345\210\270\302\267I.webp" differ
diff --git "a/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2054\345\205\221\346\215\242\345\210\270\302\267I.webp" "b/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2054\345\205\221\346\215\242\345\210\270\302\267I.webp"
new file mode 100644
index 000000000..2e62333bc
Binary files /dev/null and "b/ui/public/depot/\345\205\254\345\274\200\346\213\233\345\213\237\342\230\2054\345\205\221\346\215\242\345\210\270\302\267I.webp" differ
diff --git "a/ui/public/depot/\345\206\260\351\205\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\206\260\351\205\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..65ad8743c
Binary files /dev/null and "b/ui/public/depot/\345\206\260\351\205\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..dc39d17da
Binary files /dev/null and "b/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1f9fe334e
Binary files /dev/null and "b/ui/public/depot/\345\207\233\345\206\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\207\233\350\247\206\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\207\233\350\247\206\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..dd1994a20
Binary files /dev/null and "b/ui/public/depot/\345\207\233\350\247\206\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\207\235\350\203\266.webp" "b/ui/public/depot/\345\207\235\350\203\266.webp"
new file mode 100644
index 000000000..440656f90
Binary files /dev/null and "b/ui/public/depot/\345\207\235\350\203\266.webp" differ
diff --git "a/ui/public/depot/\345\207\257\345\260\224\345\270\214\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\207\257\345\260\224\345\270\214\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..01f0ccd7b
Binary files /dev/null and "b/ui/public/depot/\345\207\257\345\260\224\345\270\214\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\207\345\211\212\345\216\237\346\266\262.webp" "b/ui/public/depot/\345\210\207\345\211\212\345\216\237\346\266\262.webp"
new file mode 100644
index 000000000..6a15090cb
Binary files /dev/null and "b/ui/public/depot/\345\210\207\345\211\212\345\216\237\346\266\262.webp" differ
diff --git "a/ui/public/depot/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" "b/ui/public/depot/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp"
new file mode 100644
index 000000000..f4bec1773
Binary files /dev/null and "b/ui/public/depot/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" differ
diff --git "a/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..be3657ca1
Binary files /dev/null and "b/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..75826c446
Binary files /dev/null and "b/ui/public/depot/\345\210\235\351\233\252\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\251\345\210\203\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\345\210\251\345\210\203\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..b74b75de7
Binary files /dev/null and "b/ui/public/depot/\345\210\251\345\210\203\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\345\210\272\347\216\253\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\272\347\216\253\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1c1fcd3e1
Binary files /dev/null and "b/ui/public/depot/\345\210\272\347\216\253\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0fb900bee
Binary files /dev/null and "b/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5df55450d
Binary files /dev/null and "b/ui/public/depot/\345\210\273\344\277\204\346\237\217\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..138cf5517
Binary files /dev/null and "b/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b88ebedc1
Binary files /dev/null and "b/ui/public/depot/\345\210\273\345\210\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\212\240\346\200\245\350\256\270\345\217\257.webp" "b/ui/public/depot/\345\212\240\346\200\245\350\256\270\345\217\257.webp"
new file mode 100644
index 000000000..3748dd9b8
Binary files /dev/null and "b/ui/public/depot/\345\212\240\346\200\245\350\256\270\345\217\257.webp" differ
diff --git "a/ui/public/depot/\345\214\226\345\220\210\345\210\207\345\211\212\346\266\262.webp" "b/ui/public/depot/\345\214\226\345\220\210\345\210\207\345\211\212\346\266\262.webp"
new file mode 100644
index 000000000..043089f56
Binary files /dev/null and "b/ui/public/depot/\345\214\226\345\220\210\345\210\207\345\211\212\346\266\262.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\224\237\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\214\273\347\224\237\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d80927a93
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\224\237\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\214\273\347\226\227\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..369e0a3ec
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..f9c083cdc
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..de858ba79
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..4d72fc29d
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\345\214\273\347\226\227\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..113f333f5
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\214\273\347\226\227\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..54ce97247
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207.webp" "b/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..5eef3a3e3
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..8f4dcae8f
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\345\214\273\347\226\227\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\214\273\347\226\227\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a1c6bab93
Binary files /dev/null and "b/ui/public/depot/\345\214\273\347\226\227\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\201\350\277\236\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\215\201\350\277\236\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..d700a51c5
Binary files /dev/null and "b/ui/public/depot/\345\215\201\350\277\236\344\270\255\345\235\232\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\215\201\350\277\236\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\215\201\350\277\236\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..1003bf507
Binary files /dev/null and "b/ui/public/depot/\345\215\201\350\277\236\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\215\212\350\207\252\347\204\266\346\272\266\345\211\202.webp" "b/ui/public/depot/\345\215\212\350\207\252\347\204\266\346\272\266\345\211\202.webp"
new file mode 100644
index 000000000..4902cb9c6
Binary files /dev/null and "b/ui/public/depot/\345\215\212\350\207\252\347\204\266\346\272\266\345\211\202.webp" differ
diff --git "a/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..429f3d83e
Binary files /dev/null and "b/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..498a816d1
Binary files /dev/null and "b/ui/public/depot/\345\215\216\346\263\225\347\220\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ef4e429de
Binary files /dev/null and "b/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2e18b91f6
Binary files /dev/null and "b/ui/public/depot/\345\215\241\345\244\253\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\346\266\205\345\210\251\345\256\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\346\266\205\345\210\251\345\256\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a794a6dd9
Binary files /dev/null and "b/ui/public/depot/\345\215\241\346\266\205\345\210\251\345\256\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fce04aad0
Binary files /dev/null and "b/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..61dae32d4
Binary files /dev/null and "b/ui/public/depot/\345\215\241\347\274\207\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7e556a709
Binary files /dev/null and "b/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2c818fd59
Binary files /dev/null and "b/ui/public/depot/\345\215\241\350\276\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..56c77e021
Binary files /dev/null and "b/ui/public/depot/\345\216\206\351\230\265\351\224\220\346\236\252\350\212\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\214\346\234\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\214\346\234\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..97419c0e3
Binary files /dev/null and "b/ui/public/depot/\345\217\214\346\234\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\214\346\236\201\347\272\263\347\261\263\347\211\207.webp" "b/ui/public/depot/\345\217\214\346\236\201\347\272\263\347\261\263\347\211\207.webp"
new file mode 100644
index 000000000..8d86f226d
Binary files /dev/null and "b/ui/public/depot/\345\217\214\346\236\201\347\272\263\347\261\263\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\217\214\351\205\256.webp" "b/ui/public/depot/\345\217\214\351\205\256.webp"
new file mode 100644
index 000000000..622ace697
Binary files /dev/null and "b/ui/public/depot/\345\217\214\351\205\256.webp" differ
diff --git "a/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b05b57920
Binary files /dev/null and "b/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ddc160229
Binary files /dev/null and "b/ui/public/depot/\345\217\244\347\261\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9d1e327d1
Binary files /dev/null and "b/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ae5efe5e4
Binary files /dev/null and "b/ui/public/depot/\345\217\257\351\242\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c7fc65cf4
Binary files /dev/null and "b/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f2bb24eba
Binary files /dev/null and "b/ui/public/depot/\345\217\262\345\260\224\347\211\271\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a599faf3c
Binary files /dev/null and "b/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b553d0196
Binary files /dev/null and "b/ui/public/depot/\345\217\262\351\203\275\345\215\216\345\276\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\217\267\350\247\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\217\267\350\247\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4d36b43cf
Binary files /dev/null and "b/ui/public/depot/\345\217\267\350\247\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\220\210\346\210\220\347\216\211.webp" "b/ui/public/depot/\345\220\210\346\210\220\347\216\211.webp"
new file mode 100644
index 000000000..5bd3a3f5c
Binary files /dev/null and "b/ui/public/depot/\345\220\210\346\210\220\347\216\211.webp" differ
diff --git "a/ui/public/depot/\345\220\210\347\272\246\350\265\217\351\207\221.webp" "b/ui/public/depot/\345\220\210\347\272\246\350\265\217\351\207\221.webp"
new file mode 100644
index 000000000..6bfdb0153
Binary files /dev/null and "b/ui/public/depot/\345\220\210\347\272\246\350\265\217\351\207\221.webp" differ
diff --git "a/ui/public/depot/\345\220\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\220\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..12631dbd0
Binary files /dev/null and "b/ui/public/depot/\345\220\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\220\275\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\220\275\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..acf53fe81
Binary files /dev/null and "b/ui/public/depot/\345\220\275\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\221\250\345\271\264\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\221\250\345\271\264\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..30a95fd6c
Binary files /dev/null and "b/ui/public/depot/\345\221\250\345\271\264\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\222\214\345\274\246\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\222\214\345\274\246\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..059b33d34
Binary files /dev/null and "b/ui/public/depot/\345\222\214\345\274\246\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\223\210\346\264\233\345\276\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\223\210\346\264\233\345\276\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..62b9b8ad9
Binary files /dev/null and "b/ui/public/depot/\345\223\210\346\264\233\345\276\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7ab55fd5e
Binary files /dev/null and "b/ui/public/depot/\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b913431c3
Binary files /dev/null and "b/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a79e5b43d
Binary files /dev/null and "b/ui/public/depot/\345\233\233\346\234\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\233\240\351\231\200\347\275\227\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\233\240\351\231\200\347\275\227\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8d1ec2d59
Binary files /dev/null and "b/ui/public/depot/\345\233\240\351\231\200\347\275\227\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\233\272\345\214\226\347\272\244\347\273\264\346\235\277.webp" "b/ui/public/depot/\345\233\272\345\214\226\347\272\244\347\273\264\346\235\277.webp"
new file mode 100644
index 000000000..b3b1f06fb
Binary files /dev/null and "b/ui/public/depot/\345\233\272\345\214\226\347\272\244\347\273\264\346\235\277.webp" differ
diff --git "a/ui/public/depot/\345\233\272\346\272\220\345\262\251.webp" "b/ui/public/depot/\345\233\272\346\272\220\345\262\251.webp"
new file mode 100644
index 000000000..485e56b29
Binary files /dev/null and "b/ui/public/depot/\345\233\272\346\272\220\345\262\251.webp" differ
diff --git "a/ui/public/depot/\345\233\272\346\272\220\345\262\251\347\273\204.webp" "b/ui/public/depot/\345\233\272\346\272\220\345\262\251\347\273\204.webp"
new file mode 100644
index 000000000..91c148cc2
Binary files /dev/null and "b/ui/public/depot/\345\233\272\346\272\220\345\262\251\347\273\204.webp" differ
diff --git "a/ui/public/depot/\345\233\276\350\200\266\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\233\276\350\200\266\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ff1dc704e
Binary files /dev/null and "b/ui/public/depot/\345\233\276\350\200\266\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2340e9345
Binary files /dev/null and "b/ui/public/depot/\345\234\243\347\272\246\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..31484a6ec
Binary files /dev/null and "b/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2f9c54a32
Binary files /dev/null and "b/ui/public/depot/\345\234\260\347\201\265\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\235\232\351\233\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\235\232\351\233\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9ffe0d16b
Binary files /dev/null and "b/ui/public/depot/\345\235\232\351\233\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\237\203\346\213\211\346\211\230\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\237\203\346\213\211\346\211\230\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a7b86d0fb
Binary files /dev/null and "b/ui/public/depot/\345\237\203\346\213\211\346\211\230\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.webp" "b/ui/public/depot/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.webp"
new file mode 100644
index 000000000..c9eab1e80
Binary files /dev/null and "b/ui/public/depot/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.webp" differ
diff --git "a/ui/public/depot/\345\237\272\347\241\200\345\212\240\345\233\272\345\273\272\346\235\220.webp" "b/ui/public/depot/\345\237\272\347\241\200\345\212\240\345\233\272\345\273\272\346\235\220.webp"
new file mode 100644
index 000000000..060f4cefd
Binary files /dev/null and "b/ui/public/depot/\345\237\272\347\241\200\345\212\240\345\233\272\345\273\272\346\235\220.webp" differ
diff --git "a/ui/public/depot/\345\241\221\345\277\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\241\221\345\277\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6f55c9895
Binary files /dev/null and "b/ui/public/depot/\345\241\221\345\277\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5a8eddd56
Binary files /dev/null and "b/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5f6bebbd1
Binary files /dev/null and "b/ui/public/depot/\345\241\236\351\233\267\345\250\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\217\346\240\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\217\346\240\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..26ddfc9fe
Binary files /dev/null and "b/ui/public/depot/\345\244\217\346\240\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\225\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\225\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6456234ef
Binary files /dev/null and "b/ui/public/depot/\345\244\225\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\232\350\220\235\350\245\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\232\350\220\235\350\245\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9c7a825ac
Binary files /dev/null and "b/ui/public/depot/\345\244\232\350\220\235\350\245\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5aae6b908
Binary files /dev/null and "b/ui/public/depot/\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\345\215\212\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\345\215\212\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b1dec79c5
Binary files /dev/null and "b/ui/public/depot/\345\244\234\345\215\212\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..75ac949b9
Binary files /dev/null and "b/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3e4725eac
Binary files /dev/null and "b/ui/public/depot/\345\244\234\347\203\237\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f2a53b91a
Binary files /dev/null and "b/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..46e3fe059
Binary files /dev/null and "b/ui/public/depot/\345\244\234\350\216\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..01d6e025f
Binary files /dev/null and "b/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7c34ebe16
Binary files /dev/null and "b/ui/public/depot/\345\244\234\351\255\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9e5186a99
Binary files /dev/null and "b/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..500731afd
Binary files /dev/null and "b/ui/public/depot/\345\244\251\347\201\253\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fe2bbcd4a
Binary files /dev/null and "b/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cdacd9b5f
Binary files /dev/null and "b/ui/public/depot/\345\245\245\346\226\257\345\241\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\245\245\350\276\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\245\245\350\276\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e3d09ce04
Binary files /dev/null and "b/ui/public/depot/\345\245\245\350\276\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\245\275\345\245\275\345\220\203\351\245\255\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\245\275\345\245\275\345\220\203\351\245\255\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..1cf65887b
Binary files /dev/null and "b/ui/public/depot/\345\245\275\345\245\275\345\220\203\351\245\255\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\246\202\346\255\273\344\272\246\347\273\210\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\246\202\346\255\273\344\272\246\347\273\210\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..33735634b
Binary files /dev/null and "b/ui/public/depot/\345\246\202\346\255\273\344\272\246\347\273\210\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\246\256\350\212\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\246\256\350\212\231\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ad04ede09
Binary files /dev/null and "b/ui/public/depot/\345\246\256\350\212\231\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\250\234\344\273\201\345\233\276\344\272\232\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\250\234\344\273\201\345\233\276\344\272\232\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..832e71474
Binary files /dev/null and "b/ui/public/depot/\345\250\234\344\273\201\345\233\276\344\272\232\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\255\220\346\234\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\255\220\346\234\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e0f68bd21
Binary files /dev/null and "b/ui/public/depot/\345\255\220\346\234\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\255\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\255\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..379c0f478
Binary files /dev/null and "b/ui/public/depot/\345\255\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\255\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\255\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8adbac149
Binary files /dev/null and "b/ui/public/depot/\345\255\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eba8e2430
Binary files /dev/null and "b/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0e541e97d
Binary files /dev/null and "b/ui/public/depot/\345\256\210\346\236\227\344\272\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..814fd2897
Binary files /dev/null and "b/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b0aa40c89
Binary files /dev/null and "b/ui/public/depot/\345\256\211\345\223\262\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\345\276\267\345\210\207\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\345\276\267\345\210\207\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..46f7f7d4b
Binary files /dev/null and "b/ui/public/depot/\345\256\211\345\276\267\345\210\207\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..40236d2db
Binary files /dev/null and "b/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..846b1b0d6
Binary files /dev/null and "b/ui/public/depot/\345\256\211\346\257\224\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b54c2d930
Binary files /dev/null and "b/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d02366541
Binary files /dev/null and "b/ui/public/depot/\345\256\211\346\264\201\350\216\211\345\250\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4d8e78232
Binary files /dev/null and "b/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bdcaaf787
Binary files /dev/null and "b/ui/public/depot/\345\256\211\350\265\233\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2a80cbc78
Binary files /dev/null and "b/ui/public/depot/\345\256\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\264\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\256\264\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a3f36d9d8
Binary files /dev/null and "b/ui/public/depot/\345\256\264\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\256\266\345\205\267\351\233\266\344\273\266.webp" "b/ui/public/depot/\345\256\266\345\205\267\351\233\266\344\273\266.webp"
new file mode 100644
index 000000000..5c0dfb785
Binary files /dev/null and "b/ui/public/depot/\345\256\266\345\205\267\351\233\266\344\273\266.webp" differ
diff --git "a/ui/public/depot/\345\257\222\346\252\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\257\222\346\252\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a7ef97c8d
Binary files /dev/null and "b/ui/public/depot/\345\257\222\346\252\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..512e6f86a
Binary files /dev/null and "b/ui/public/depot/\345\257\222\350\212\222\345\205\213\346\264\233\344\270\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\257\273\346\230\274\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\345\257\273\346\230\274\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..0ff4c7086
Binary files /dev/null and "b/ui/public/depot/\345\257\273\346\230\274\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..9eea01a45
Binary files /dev/null and "b/ui/public/depot/\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\345\257\273\350\256\277\345\217\202\346\225\260\346\250\241\345\236\213.webp" "b/ui/public/depot/\345\257\273\350\256\277\345\217\202\346\225\260\346\250\241\345\236\213.webp"
new file mode 100644
index 000000000..ba462d093
Binary files /dev/null and "b/ui/public/depot/\345\257\273\350\256\277\345\217\202\346\225\260\346\250\241\345\236\213.webp" differ
diff --git "a/ui/public/depot/\345\257\273\350\256\277\346\225\260\346\215\256\345\245\221\347\272\246.webp" "b/ui/public/depot/\345\257\273\350\256\277\346\225\260\346\215\256\345\245\221\347\272\246.webp"
new file mode 100644
index 000000000..ab3237f67
Binary files /dev/null and "b/ui/public/depot/\345\257\273\350\256\277\346\225\260\346\215\256\345\245\221\347\272\246.webp" differ
diff --git "a/ui/public/depot/\345\257\274\347\201\253\347\264\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\257\274\347\201\253\347\264\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6bba8584e
Binary files /dev/null and "b/ui/public/depot/\345\257\274\347\201\253\347\264\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\260\217\346\273\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\260\217\346\273\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..25ce1b5f0
Binary files /dev/null and "b/ui/public/depot/\345\260\217\346\273\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\260\230\347\216\257\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\345\260\230\347\216\257\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..7add8496d
Binary files /dev/null and "b/ui/public/depot/\345\260\230\347\216\257\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..60c2669fa
Binary files /dev/null and "b/ui/public/depot/\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\261\261\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\261\261\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2eeee9739
Binary files /dev/null and "b/ui/public/depot/\345\261\261\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\262\201\350\277\207\345\215\216\347\201\257.webp" "b/ui/public/depot/\345\262\201\350\277\207\345\215\216\347\201\257.webp"
new file mode 100644
index 000000000..fcf81000e
Binary files /dev/null and "b/ui/public/depot/\345\262\201\350\277\207\345\215\216\347\201\257.webp" differ
diff --git "a/ui/public/depot/\345\263\257\350\201\224\350\264\270\346\230\223\347\211\251\346\265\201\350\241\245\347\273\231.webp" "b/ui/public/depot/\345\263\257\350\201\224\350\264\270\346\230\223\347\211\251\346\265\201\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..3ce2e31b5
Binary files /dev/null and "b/ui/public/depot/\345\263\257\350\201\224\350\264\270\346\230\223\347\211\251\346\265\201\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f1e7ad93e
Binary files /dev/null and "b/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b8a3a7a00
Binary files /dev/null and "b/ui/public/depot/\345\264\226\345\277\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bb093246e
Binary files /dev/null and "b/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..69c621b7e
Binary files /dev/null and "b/ui/public/depot/\345\265\257\345\263\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\267\241\346\236\227\350\200\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\267\241\346\236\227\350\200\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f4de914a3
Binary files /dev/null and "b/ui/public/depot/\345\267\241\346\236\227\350\200\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\267\246\344\271\220\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\267\246\344\271\220\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..94023f72f
Binary files /dev/null and "b/ui/public/depot/\345\267\246\344\271\220\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e453c5d7a
Binary files /dev/null and "b/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1fde9c4df
Binary files /dev/null and "b/ui/public/depot/\345\267\253\346\201\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\270\203\344\270\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\270\203\344\270\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..65daa8025
Binary files /dev/null and "b/ui/public/depot/\345\270\203\344\270\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..429ac2767
Binary files /dev/null and "b/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2b9617fc4
Binary files /dev/null and "b/ui/public/depot/\345\270\203\346\264\233\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\270\225\346\213\211\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\270\225\346\213\211\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..78ee89080
Binary files /dev/null and "b/ui/public/depot/\345\270\225\346\213\211\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\271\262\345\221\230\345\205\221\346\215\242\345\210\270.webp" "b/ui/public/depot/\345\271\262\345\221\230\345\205\221\346\215\242\345\210\270.webp"
new file mode 100644
index 000000000..5af4b84df
Binary files /dev/null and "b/ui/public/depot/\345\271\262\345\221\230\345\205\221\346\215\242\345\210\270.webp" differ
diff --git "a/ui/public/depot/\345\271\264\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\271\264\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f1d631a12
Binary files /dev/null and "b/ui/public/depot/\345\271\264\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..52d3233a0
Binary files /dev/null and "b/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d82c784dc
Binary files /dev/null and "b/ui/public/depot/\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\272\206\345\205\270\347\244\274\347\233\222.webp" "b/ui/public/depot/\345\272\206\345\205\270\347\244\274\347\233\222.webp"
new file mode 100644
index 000000000..885231a73
Binary files /dev/null and "b/ui/public/depot/\345\272\206\345\205\270\347\244\274\347\233\222.webp" differ
diff --git "a/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\212\240\345\274\272\345\211\202.webp" "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\212\240\345\274\272\345\211\202.webp"
new file mode 100644
index 000000000..d57f7614b
Binary files /dev/null and "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\212\240\345\274\272\345\211\202.webp" differ
diff --git "a/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\260\217\346\240\267.webp" "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\260\217\346\240\267.webp"
new file mode 100644
index 000000000..494b5376e
Binary files /dev/null and "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\345\260\217\346\240\267.webp" differ
diff --git "a/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\346\265\223\347\274\251\346\266\262.webp" "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\346\265\223\347\274\251\346\266\262.webp"
new file mode 100644
index 000000000..bc9be1563
Binary files /dev/null and "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\346\265\223\347\274\251\346\266\262.webp" differ
diff --git "a/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\351\241\266\346\266\262.webp" "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\351\241\266\346\266\262.webp"
new file mode 100644
index 000000000..f8f30648e
Binary files /dev/null and "b/ui/public/depot/\345\272\224\346\200\245\347\220\206\346\231\272\351\241\266\346\266\262.webp" differ
diff --git "a/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3390e8d36
Binary files /dev/null and "b/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..64a39487d
Binary files /dev/null and "b/ui/public/depot/\345\274\202\345\256\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\274\202\351\223\201.webp" "b/ui/public/depot/\345\274\202\351\223\201.webp"
new file mode 100644
index 000000000..2fab314d8
Binary files /dev/null and "b/ui/public/depot/\345\274\202\351\223\201.webp" differ
diff --git "a/ui/public/depot/\345\274\202\351\223\201\345\235\227.webp" "b/ui/public/depot/\345\274\202\351\223\201\345\235\227.webp"
new file mode 100644
index 000000000..86db4a9db
Binary files /dev/null and "b/ui/public/depot/\345\274\202\351\223\201\345\235\227.webp" differ
diff --git "a/ui/public/depot/\345\274\202\351\223\201\347\242\216\347\211\207.webp" "b/ui/public/depot/\345\274\202\351\223\201\347\242\216\347\211\207.webp"
new file mode 100644
index 000000000..6ca47e835
Binary files /dev/null and "b/ui/public/depot/\345\274\202\351\223\201\347\242\216\347\211\207.webp" differ
diff --git "a/ui/public/depot/\345\274\202\351\223\201\347\273\204.webp" "b/ui/public/depot/\345\274\202\351\223\201\347\273\204.webp"
new file mode 100644
index 000000000..cbf97259f
Binary files /dev/null and "b/ui/public/depot/\345\274\202\351\223\201\347\273\204.webp" differ
diff --git "a/ui/public/depot/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..af0a2efdb
Binary files /dev/null and "b/ui/public/depot/\345\275\222\346\272\237\345\271\275\347\201\265\351\262\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\276\256\351\243\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\276\256\351\243\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..72f8036f1
Binary files /dev/null and "b/ui/public/depot/\345\276\256\351\243\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ed2072689
Binary files /dev/null and "b/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b4e7dd4f8
Binary files /dev/null and "b/ui/public/depot/\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\203\205\346\212\245\345\207\255\350\257\201.webp" "b/ui/public/depot/\346\203\205\346\212\245\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..2c803ab0a
Binary files /dev/null and "b/ui/public/depot/\346\203\205\346\212\245\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bf60f0f05
Binary files /dev/null and "b/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2d5031576
Binary files /dev/null and "b/ui/public/depot/\346\203\212\350\233\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp" "b/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..30a95fd6c
Binary files /dev/null and "b/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\345\271\262\345\221\230\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..1158038ff
Binary files /dev/null and "b/ui/public/depot/\346\204\237\350\260\242\345\272\206\345\205\270\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a2eba8966
Binary files /dev/null and "b/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..248ead7a7
Binary files /dev/null and "b/ui/public/depot/\346\205\221\347\240\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..83be08cee
Binary files /dev/null and "b/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0148bdcdc
Binary files /dev/null and "b/ui/public/depot/\346\205\225\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\210\230\350\275\246\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\210\230\350\275\246\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..30d982f19
Binary files /dev/null and "b/ui/public/depot/\346\210\230\350\275\246\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\210\252\344\272\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\210\252\344\272\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ca63030b1
Binary files /dev/null and "b/ui/public/depot/\346\210\252\344\272\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\210\264\350\217\262\346\201\251\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\210\264\350\217\262\346\201\251\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b7116a9c5
Binary files /dev/null and "b/ui/public/depot/\346\210\264\350\217\262\346\201\251\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\211\255\350\275\254\351\206\207.webp" "b/ui/public/depot/\346\211\255\350\275\254\351\206\207.webp"
new file mode 100644
index 000000000..3aeae2c77
Binary files /dev/null and "b/ui/public/depot/\346\211\255\350\275\254\351\206\207.webp" differ
diff --git "a/ui/public/depot/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..410205b64
Binary files /dev/null and "b/ui/public/depot/\346\211\277\346\233\246\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.webp" "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.webp"
new file mode 100644
index 000000000..9a6483e14
Binary files /dev/null and "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.webp" differ
diff --git "a/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.webp" "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.webp"
new file mode 100644
index 000000000..a8121f32f
Binary files /dev/null and "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.webp" differ
diff --git "a/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2673.webp" "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2673.webp"
new file mode 100644
index 000000000..7ebb3427a
Binary files /dev/null and "b/ui/public/depot/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2673.webp" differ
diff --git "a/ui/public/depot/\346\212\200\346\234\257\350\260\203\346\237\245\350\241\245\347\273\231.webp" "b/ui/public/depot/\346\212\200\346\234\257\350\260\203\346\237\245\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..e76d0410a
Binary files /dev/null and "b/ui/public/depot/\346\212\200\346\234\257\350\260\203\346\237\245\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\346\212\230\345\205\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\212\230\345\205\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5ca50faec
Binary files /dev/null and "b/ui/public/depot/\346\212\230\345\205\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fce28cd88
Binary files /dev/null and "b/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4e78d0f78
Binary files /dev/null and "b/ui/public/depot/\346\213\211\346\231\256\345\205\260\345\276\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\213\233\350\201\230\350\256\270\345\217\257.webp" "b/ui/public/depot/\346\213\233\350\201\230\350\256\270\345\217\257.webp"
new file mode 100644
index 000000000..f3df2aff8
Binary files /dev/null and "b/ui/public/depot/\346\213\233\350\201\230\350\256\270\345\217\257.webp" differ
diff --git "a/ui/public/depot/\346\213\234\346\235\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\213\234\346\235\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..656c943af
Binary files /dev/null and "b/ui/public/depot/\346\213\234\346\235\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\216\240\351\243\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\216\240\351\243\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2d5291e8b
Binary files /dev/null and "b/ui/public/depot/\346\216\240\351\243\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..211679f00
Binary files /dev/null and "b/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9368c1645
Binary files /dev/null and "b/ui/public/depot/\346\216\250\350\277\233\344\271\213\347\216\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\217\220\344\270\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\217\220\344\270\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3f25cc266
Binary files /dev/null and "b/ui/public/depot/\346\217\220\344\270\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\217\220\347\272\257\346\272\220\345\262\251.webp" "b/ui/public/depot/\346\217\220\347\272\257\346\272\220\345\262\251.webp"
new file mode 100644
index 000000000..5997dea5e
Binary files /dev/null and "b/ui/public/depot/\346\217\220\347\272\257\346\272\220\345\262\251.webp" differ
diff --git "a/ui/public/depot/\346\221\251\346\240\271\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\221\251\346\240\271\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f08b4f9e3
Binary files /dev/null and "b/ui/public/depot/\346\221\251\346\240\271\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\224\271\351\207\217\350\243\205\347\275\256.webp" "b/ui/public/depot/\346\224\271\351\207\217\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..72bb4cebf
Binary files /dev/null and "b/ui/public/depot/\346\224\271\351\207\217\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\344\273\252.webp" "b/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\344\273\252.webp"
new file mode 100644
index 000000000..77b177610
Binary files /dev/null and "b/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\344\273\252.webp" differ
diff --git "a/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\346\235\241.webp" "b/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\346\235\241.webp"
new file mode 100644
index 000000000..2699316b2
Binary files /dev/null and "b/ui/public/depot/\346\225\260\346\215\256\345\242\236\350\241\245\346\235\241.webp" differ
diff --git "a/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..24daa15a3
Binary files /dev/null and "b/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a04cfa116
Binary files /dev/null and "b/ui/public/depot/\346\226\221\347\202\271\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\245\347\275\252\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\245\347\275\252\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5c0545523
Binary files /dev/null and "b/ui/public/depot/\346\226\245\347\275\252\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..934b1d485
Binary files /dev/null and "b/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..434700469
Binary files /dev/null and "b/ui/public/depot/\346\226\255\345\264\226\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ec1b49274
Binary files /dev/null and "b/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d3d50e363
Binary files /dev/null and "b/ui/public/depot/\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c75556f2e
Binary files /dev/null and "b/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e57fb647c
Binary files /dev/null and "b/ui/public/depot/\346\227\251\351\234\262\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\227\266\350\243\205\350\207\252\351\200\211\345\207\255\350\257\201.webp" "b/ui/public/depot/\346\227\266\350\243\205\350\207\252\351\200\211\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..5f628d792
Binary files /dev/null and "b/ui/public/depot/\346\227\266\350\243\205\350\207\252\351\200\211\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\346\230\216\346\244\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\216\346\244\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..04691398b
Binary files /dev/null and "b/ui/public/depot/\346\230\216\346\244\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0c66ae108
Binary files /dev/null and "b/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cbf28fb54
Binary files /dev/null and "b/ui/public/depot/\346\230\237\346\236\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\230\237\346\272\220\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\237\346\272\220\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e33dce5c7
Binary files /dev/null and "b/ui/public/depot/\346\230\237\346\272\220\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d1f0481aa
Binary files /dev/null and "b/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b4ddb24ca
Binary files /dev/null and "b/ui/public/depot/\346\230\237\347\206\212\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\231\223\346\255\214\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\231\223\346\255\214\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..273bb370a
Binary files /dev/null and "b/ui/public/depot/\346\231\223\346\255\214\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fa2770a80
Binary files /dev/null and "b/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9a2b92faf
Binary files /dev/null and "b/ui/public/depot/\346\231\256\347\275\227\346\227\272\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\231\266\344\275\223\345\205\203\344\273\266.webp" "b/ui/public/depot/\346\231\266\344\275\223\345\205\203\344\273\266.webp"
new file mode 100644
index 000000000..1e12cce7f
Binary files /dev/null and "b/ui/public/depot/\346\231\266\344\275\223\345\205\203\344\273\266.webp" differ
diff --git "a/ui/public/depot/\346\231\266\344\275\223\345\220\210\347\272\246\350\265\217\351\207\221.webp" "b/ui/public/depot/\346\231\266\344\275\223\345\220\210\347\272\246\350\265\217\351\207\221.webp"
new file mode 100644
index 000000000..abb811d17
Binary files /dev/null and "b/ui/public/depot/\346\231\266\344\275\223\345\220\210\347\272\246\350\265\217\351\207\221.webp" differ
diff --git "a/ui/public/depot/\346\231\266\344\275\223\347\224\265\345\255\220\345\215\225\345\205\203.webp" "b/ui/public/depot/\346\231\266\344\275\223\347\224\265\345\255\220\345\215\225\345\205\203.webp"
new file mode 100644
index 000000000..0252d1482
Binary files /dev/null and "b/ui/public/depot/\346\231\266\344\275\223\347\224\265\345\255\220\345\215\225\345\205\203.webp" differ
diff --git "a/ui/public/depot/\346\231\266\344\275\223\347\224\265\350\267\257.webp" "b/ui/public/depot/\346\231\266\344\275\223\347\224\265\350\267\257.webp"
new file mode 100644
index 000000000..e49ffc495
Binary files /dev/null and "b/ui/public/depot/\346\231\266\344\275\223\347\224\265\350\267\257.webp" differ
diff --git "a/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d8b95e8f8
Binary files /dev/null and "b/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..838c802ef
Binary files /dev/null and "b/ui/public/depot/\346\232\227\347\264\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\232\256\350\220\275\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\232\256\350\220\275\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b850f9c95
Binary files /dev/null and "b/ui/public/depot/\346\232\256\350\220\275\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\232\264\350\241\214\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\232\264\350\241\214\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ddf9aff62
Binary files /dev/null and "b/ui/public/depot/\346\232\264\350\241\214\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\232\264\351\233\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\232\264\351\233\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7dbe4654f
Binary files /dev/null and "b/ui/public/depot/\346\232\264\351\233\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..db8885bd3
Binary files /dev/null and "b/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fb7b59f22
Binary files /dev/null and "b/ui/public/depot/\346\234\210\347\246\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..991b6a8f5
Binary files /dev/null and "b/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fe1fe3207
Binary files /dev/null and "b/ui/public/depot/\346\234\210\350\247\201\345\244\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..581869994
Binary files /dev/null and "b/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6ac09455b
Binary files /dev/null and "b/ui/public/depot/\346\234\253\350\215\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\257\345\270\210\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..66b732cc3
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..89fe92561
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..66d338db4
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..638562d9a
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\346\234\257\345\270\210\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..bf8eca398
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\257\345\270\210\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5e59cc982
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207.webp" "b/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..f0e2dc37d
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..67f35e38d
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\346\234\257\345\270\210\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\234\257\345\270\210\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2225f632c
Binary files /dev/null and "b/ui/public/depot/\346\234\257\345\270\210\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\217\344\273\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\217\344\273\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f14b94093
Binary files /dev/null and "b/ui/public/depot/\346\235\217\344\273\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..843458e3b
Binary files /dev/null and "b/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4f6f93cea
Binary files /dev/null and "b/ui/public/depot/\346\235\234\345\256\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\234\346\236\227\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\234\346\236\227\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6f3308ae3
Binary files /dev/null and "b/ui/public/depot/\346\235\234\346\236\227\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cf59c924c
Binary files /dev/null and "b/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7623561b9
Binary files /dev/null and "b/ui/public/depot/\346\235\260\345\205\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eac63fba4
Binary files /dev/null and "b/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2e35e5e5d
Binary files /dev/null and "b/ui/public/depot/\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b69cd8a95
Binary files /dev/null and "b/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b4f7a7b77
Binary files /dev/null and "b/ui/public/depot/\346\235\276\346\236\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\235\276\347\203\237\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\346\235\276\347\203\237\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..2c2da641e
Binary files /dev/null and "b/ui/public/depot/\346\235\276\347\203\237\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\346\236\201\345\205\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\236\201\345\205\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3fe9444dc
Binary files /dev/null and "b/ui/public/depot/\346\236\201\345\205\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b17727a00
Binary files /dev/null and "b/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f73809b68
Binary files /dev/null and "b/ui/public/depot/\346\236\201\345\242\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\236\227\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\236\227\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e4ac855c7
Binary files /dev/null and "b/ui/public/depot/\346\236\227\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\237\217\345\226\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\237\217\345\226\231\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..daf3a61d5
Binary files /dev/null and "b/ui/public/depot/\346\237\217\345\226\231\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\237\245\350\256\277\350\241\245\347\273\231.webp" "b/ui/public/depot/\346\237\245\350\256\277\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..9d48f74ce
Binary files /dev/null and "b/ui/public/depot/\346\237\245\350\256\277\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f75b5e0c4
Binary files /dev/null and "b/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..87a498389
Binary files /dev/null and "b/ui/public/depot/\346\240\274\345\212\263\345\205\213\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\240\274\346\213\211\345\260\274\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\240\274\346\213\211\345\260\274\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6d685bcce
Binary files /dev/null and "b/ui/public/depot/\346\240\274\346\213\211\345\260\274\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9bb906b7e
Binary files /dev/null and "b/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..87c316f8a
Binary files /dev/null and "b/ui/public/depot/\346\240\274\351\233\267\344\274\212\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d24498bd1
Binary files /dev/null and "b/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cd72a34ad
Binary files /dev/null and "b/ui/public/depot/\346\241\203\351\207\221\345\250\230\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\241\221\350\221\232\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\241\221\350\221\232\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..58d1f2d23
Binary files /dev/null and "b/ui/public/depot/\346\241\221\350\221\232\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5d8c91fdc
Binary files /dev/null and "b/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3883e79c8
Binary files /dev/null and "b/ui/public/depot/\346\242\205\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c53e66ed2
Binary files /dev/null and "b/ui/public/depot/\346\242\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eeecf2052
Binary files /dev/null and "b/ui/public/depot/\346\242\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b3541bd6d
Binary files /dev/null and "b/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e6e7b7e2f
Binary files /dev/null and "b/ui/public/depot/\346\242\223\345\205\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f5350e209
Binary files /dev/null and "b/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a5538a504
Binary files /dev/null and "b/ui/public/depot/\346\243\230\345\210\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9d5cd8f7e
Binary files /dev/null and "b/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1bc79111d
Binary files /dev/null and "b/ui/public/depot/\346\243\256\350\232\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\243\256\350\245\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\243\256\350\245\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6a6c76dfa
Binary files /dev/null and "b/ui/public/depot/\346\243\256\350\245\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..37e06f8a2
Binary files /dev/null and "b/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f49e8600d
Binary files /dev/null and "b/ui/public/depot/\346\247\220\347\220\245\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\250\241\347\273\204\346\225\260\346\215\256\345\235\227.webp" "b/ui/public/depot/\346\250\241\347\273\204\346\225\260\346\215\256\345\235\227.webp"
new file mode 100644
index 000000000..bd4b416b1
Binary files /dev/null and "b/ui/public/depot/\346\250\241\347\273\204\346\225\260\346\215\256\345\235\227.webp" differ
diff --git "a/ui/public/depot/\346\255\214\350\225\276\350\222\202\345\250\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\255\214\350\225\276\350\222\202\345\250\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f70439748
Binary files /dev/null and "b/ui/public/depot/\346\255\214\350\225\276\350\222\202\345\250\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..43e09e167
Binary files /dev/null and "b/ui/public/depot/\346\255\243\344\271\211\351\252\221\345\243\253\345\217\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\256\213\345\244\234\345\216\235\350\226\252\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\346\256\213\345\244\234\345\216\235\350\226\252\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..a1e2b34b6
Binary files /dev/null and "b/ui/public/depot/\346\256\213\345\244\234\345\216\235\350\226\252\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\346\260\264\346\234\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\260\264\346\234\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8c95008d9
Binary files /dev/null and "b/ui/public/depot/\346\260\264\346\234\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d5c0ad741
Binary files /dev/null and "b/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..816a4c9d4
Binary files /dev/null and "b/ui/public/depot/\346\263\241\346\231\256\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6e7b913ba
Binary files /dev/null and "b/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cc4ccb330
Binary files /dev/null and "b/ui/public/depot/\346\263\241\346\263\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fba943ed8
Binary files /dev/null and "b/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1741105e1
Binary files /dev/null and "b/ui/public/depot/\346\263\242\347\231\273\345\217\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f3903cf9d
Binary files /dev/null and "b/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..176f866b1
Binary files /dev/null and "b/ui/public/depot/\346\263\245\345\262\251\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..442887bc7
Binary files /dev/null and "b/ui/public/depot/\346\263\260\346\213\211\345\244\247\351\231\206\350\260\203\346\237\245\345\233\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\264\213\347\201\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\264\213\347\201\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9957c9f1d
Binary files /dev/null and "b/ui/public/depot/\346\264\213\347\201\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\264\233\346\264\233\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\264\233\346\264\233\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2fb9176e9
Binary files /dev/null and "b/ui/public/depot/\346\264\233\346\264\233\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\201\346\230\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\201\346\230\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d3aa8451e
Binary files /dev/null and "b/ui/public/depot/\346\265\201\346\230\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3299f60ff
Binary files /dev/null and "b/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..07a502d36
Binary files /dev/null and "b/ui/public/depot/\346\265\201\346\230\237\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..878695ec8
Binary files /dev/null and "b/ui/public/depot/\346\265\212\345\277\203\346\226\257\345\215\241\350\222\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\267\346\262\253\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\267\346\262\253\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c79ddf4fe
Binary files /dev/null and "b/ui/public/depot/\346\265\267\346\262\253\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\267\350\222\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\267\350\222\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1e870f0dc
Binary files /dev/null and "b/ui/public/depot/\346\265\267\350\222\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\265\267\351\234\223\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\265\267\351\234\223\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..171775b2d
Binary files /dev/null and "b/ui/public/depot/\346\265\267\351\234\223\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..68b3a7ede
Binary files /dev/null and "b/ui/public/depot/\346\266\244\347\201\253\346\235\260\350\245\277\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\254\347\276\275\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\254\347\276\275\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..59d2cc4c3
Binary files /dev/null and "b/ui/public/depot/\346\267\254\347\276\275\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\345\267\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\345\267\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3ca20a18c
Binary files /dev/null and "b/ui/public/depot/\346\267\261\345\267\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\345\276\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\345\276\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..39706d8a6
Binary files /dev/null and "b/ui/public/depot/\346\267\261\345\276\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4afeb2f73
Binary files /dev/null and "b/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1f3c46bbc
Binary files /dev/null and "b/ui/public/depot/\346\267\261\346\265\267\350\211\262\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3466427a8
Binary files /dev/null and "b/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2c08bc2af
Binary files /dev/null and "b/ui/public/depot/\346\267\261\351\235\233\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\205\346\265\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\205\346\265\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d6dd67e6b
Binary files /dev/null and "b/ui/public/depot/\346\270\205\346\265\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6b872a6dc
Binary files /dev/null and "b/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..99d559d69
Binary files /dev/null and "b/ui/public/depot/\346\270\205\351\201\223\345\244\253\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\212\351\273\230\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\346\270\212\351\273\230\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..9b4e0effa
Binary files /dev/null and "b/ui/public/depot/\346\270\212\351\273\230\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\346\270\241\346\241\245\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\241\346\241\245\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..840311c92
Binary files /dev/null and "b/ui/public/depot/\346\270\241\346\241\245\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\251\347\261\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\251\347\261\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d01ce5618
Binary files /dev/null and "b/ui/public/depot/\346\270\251\347\261\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5ab68f7b7
Binary files /dev/null and "b/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a50c555f9
Binary files /dev/null and "b/ui/public/depot/\346\270\251\350\222\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\272\220\345\262\251.webp" "b/ui/public/depot/\346\272\220\345\262\251.webp"
new file mode 100644
index 000000000..bdcaf9bca
Binary files /dev/null and "b/ui/public/depot/\346\272\220\345\262\251.webp" differ
diff --git "a/ui/public/depot/\346\272\220\347\237\263\347\242\216\347\211\207.webp" "b/ui/public/depot/\346\272\220\347\237\263\347\242\216\347\211\207.webp"
new file mode 100644
index 000000000..033139be2
Binary files /dev/null and "b/ui/public/depot/\346\272\220\347\237\263\347\242\216\347\211\207.webp" differ
diff --git "a/ui/public/depot/\346\276\204\351\227\252\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\276\204\351\227\252\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..da41aaabf
Binary files /dev/null and "b/ui/public/depot/\346\276\204\351\227\252\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\346\277\257\345\260\230\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\346\277\257\345\260\230\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..286faeca4
Binary files /dev/null and "b/ui/public/depot/\346\277\257\345\260\230\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\253\345\223\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\253\345\223\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..15b6158db
Binary files /dev/null and "b/ui/public/depot/\347\201\253\345\223\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\253\347\245\236\345\233\240\351\231\200\347\275\227\346\213\233\345\213\237\345\210\270.webp" "b/ui/public/depot/\347\201\253\347\245\236\345\233\240\351\231\200\347\275\227\346\213\233\345\213\237\345\210\270.webp"
new file mode 100644
index 000000000..4961bf510
Binary files /dev/null and "b/ui/public/depot/\347\201\253\347\245\236\345\233\240\351\231\200\347\275\227\346\213\233\345\213\237\345\210\270.webp" differ
diff --git "a/ui/public/depot/\347\201\253\347\245\236\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\253\347\245\236\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d07338ce9
Binary files /dev/null and "b/ui/public/depot/\347\201\253\347\245\236\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\253\351\276\231S\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\253\351\276\231S\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..39b2f46d6
Binary files /dev/null and "b/ui/public/depot/\347\201\253\351\276\231S\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..49f923f93
Binary files /dev/null and "b/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3b29aada7
Binary files /dev/null and "b/ui/public/depot/\347\201\260\345\226\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\260\346\257\253\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\260\346\257\253\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c0bdb4a71
Binary files /dev/null and "b/ui/public/depot/\347\201\260\346\257\253\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\260\347\203\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\260\347\203\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6e1be0382
Binary files /dev/null and "b/ui/public/depot/\347\201\260\347\203\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\201\265\347\237\245\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\201\265\347\237\245\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..dcdda9cce
Binary files /dev/null and "b/ui/public/depot/\347\201\265\347\237\245\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\202\216\345\256\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\202\216\345\256\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8101c3996
Binary files /dev/null and "b/ui/public/depot/\347\202\216\345\256\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cb74f91d5
Binary files /dev/null and "b/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..180564a8c
Binary files /dev/null and "b/ui/public/depot/\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\202\216\347\213\261\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\202\216\347\213\261\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..968bf9918
Binary files /dev/null and "b/ui/public/depot/\347\202\216\347\213\261\347\202\216\347\206\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\202\275\345\220\210\351\207\221.webp" "b/ui/public/depot/\347\202\275\345\220\210\351\207\221.webp"
new file mode 100644
index 000000000..8159f4149
Binary files /dev/null and "b/ui/public/depot/\347\202\275\345\220\210\351\207\221.webp" differ
diff --git "a/ui/public/depot/\347\202\275\345\220\210\351\207\221\345\235\227.webp" "b/ui/public/depot/\347\202\275\345\220\210\351\207\221\345\235\227.webp"
new file mode 100644
index 000000000..e37f0636a
Binary files /dev/null and "b/ui/public/depot/\347\202\275\345\220\210\351\207\221\345\235\227.webp" differ
diff --git "a/ui/public/depot/\347\203\210\345\244\217\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\203\210\345\244\217\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9b2e5405b
Binary files /dev/null and "b/ui/public/depot/\347\203\210\345\244\217\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\203\247\347\273\223\346\240\270\345\207\235\346\231\266.webp" "b/ui/public/depot/\347\203\247\347\273\223\346\240\270\345\207\235\346\231\266.webp"
new file mode 100644
index 000000000..376f773da
Binary files /dev/null and "b/ui/public/depot/\347\203\247\347\273\223\346\240\270\345\207\235\346\231\266.webp" differ
diff --git "a/ui/public/depot/\347\204\260\345\260\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\204\260\345\260\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..69302cf5d
Binary files /dev/null and "b/ui/public/depot/\347\204\260\345\260\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\204\260\345\275\261\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\204\260\345\275\261\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..be1983db8
Binary files /dev/null and "b/ui/public/depot/\347\204\260\345\275\261\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\205\214\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\205\214\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..174b9e056
Binary files /dev/null and "b/ui/public/depot/\347\205\214\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\205\214\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\205\214\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..612cdc52c
Binary files /dev/null and "b/ui/public/depot/\347\205\214\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5a9e78fcb
Binary files /dev/null and "b/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..52320227f
Binary files /dev/null and "b/ui/public/depot/\347\206\224\346\263\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\207\203\347\201\260\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\347\207\203\347\201\260\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..e0a782c14
Binary files /dev/null and "b/ui/public/depot/\347\207\203\347\201\260\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5f7dc3d69
Binary files /dev/null and "b/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ffd6ea50c
Binary files /dev/null and "b/ui/public/depot/\347\207\247\347\237\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7e47675bf
Binary files /dev/null and "b/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8d387c896
Binary files /dev/null and "b/ui/public/depot/\347\210\261\344\270\275\344\270\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\211\271\347\247\215\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4c783e3c4
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..8f1e4801c
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..c900e22a0
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..e9b024b91
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\347\211\271\347\247\215\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..892031258
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\211\271\347\247\215\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c83c9a91e
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207.webp" "b/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..d8f73ffb0
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..bf805dc2b
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\247\215\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\211\271\347\247\215\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9a58612a3
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\247\215\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\261\263\347\261\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\211\271\347\261\263\347\261\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eb8551977
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\261\263\347\261\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\211\271\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp" "b/ui/public/depot/\347\211\271\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp"
new file mode 100644
index 000000000..24dd0b99c
Binary files /dev/null and "b/ui/public/depot/\347\211\271\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\213\231\345\207\273\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b777ccabf
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..915b0fa55
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..b278ba99f
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..6ccebe4f8
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\347\213\231\345\207\273\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..9b9c976f3
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\213\231\345\207\273\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..066feb669
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207.webp" "b/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..27909e044
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..4b4f642fe
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\347\213\231\345\207\273\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\213\231\345\207\273\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2933bd132
Binary files /dev/null and "b/ui/public/depot/\347\213\231\345\207\273\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9dc16e973
Binary files /dev/null and "b/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..04bd76911
Binary files /dev/null and "b/ui/public/depot/\347\213\256\350\235\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6e96e847f
Binary files /dev/null and "b/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e8ee5a753
Binary files /dev/null and "b/ui/public/depot/\347\214\216\350\234\202\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\233\346\201\251\347\272\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\216\233\346\201\251\347\272\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d878bde54
Binary files /dev/null and "b/ui/public/depot/\347\216\233\346\201\251\347\272\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\233\351\234\262\350\245\277\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\216\233\351\234\262\350\245\277\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f43e152b0
Binary files /dev/null and "b/ui/public/depot/\347\216\233\351\234\262\350\245\277\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d25858e1f
Binary files /dev/null and "b/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4b793c3c9
Binary files /dev/null and "b/ui/public/depot/\347\216\253\345\205\260\350\216\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\253\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\216\253\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b5d073728
Binary files /dev/null and "b/ui/public/depot/\347\216\253\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\216\257\347\203\203\350\201\232\350\264\250.webp" "b/ui/public/depot/\347\216\257\347\203\203\350\201\232\350\264\250.webp"
new file mode 100644
index 000000000..2796af2cd
Binary files /dev/null and "b/ui/public/depot/\347\216\257\347\203\203\350\201\232\350\264\250.webp" differ
diff --git "a/ui/public/depot/\347\216\257\347\203\203\351\242\204\345\210\266\344\275\223.webp" "b/ui/public/depot/\347\216\257\347\203\203\351\242\204\345\210\266\344\275\223.webp"
new file mode 100644
index 000000000..7bc086df9
Binary files /dev/null and "b/ui/public/depot/\347\216\257\347\203\203\351\242\204\345\210\266\344\275\223.webp" differ
diff --git "a/ui/public/depot/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..dda607567
Binary files /dev/null and "b/ui/public/depot/\347\220\263\347\220\205\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\220\264\346\237\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\220\264\346\237\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f372053bb
Binary files /dev/null and "b/ui/public/depot/\347\220\264\346\237\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bc4fbacb4
Binary files /dev/null and "b/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..38c0c7f95
Binary files /dev/null and "b/ui/public/depot/\347\221\225\345\205\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\222\200\347\222\250\351\227\252\350\200\200\345\257\273\350\256\277\345\207\255\350\257\201.webp" "b/ui/public/depot/\347\222\200\347\222\250\351\227\252\350\200\200\345\257\273\350\256\277\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..10cfdbbe3
Binary files /dev/null and "b/ui/public/depot/\347\222\200\347\222\250\351\227\252\350\200\200\345\257\273\350\256\277\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9b5a14ba2
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5c1c0b973
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\207\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\223\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\223\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f619e32b1
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\223\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eebad6723
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..082dcdf1d
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\233\252\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..697a02f1b
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8ed9d1f76
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\235\242\351\270\256\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\231\275\351\251\254\351\206\207.webp" "b/ui/public/depot/\347\231\275\351\251\254\351\206\207.webp"
new file mode 100644
index 000000000..0d2a4ee47
Binary files /dev/null and "b/ui/public/depot/\347\231\275\351\251\254\351\206\207.webp" differ
diff --git "a/ui/public/depot/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a0fa93f0c
Binary files /dev/null and "b/ui/public/depot/\347\231\276\347\202\274\345\230\211\347\273\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..983418932
Binary files /dev/null and "b/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..da57b3be3
Binary files /dev/null and "b/ui/public/depot/\347\234\237\347\220\206\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..02198509b
Binary files /dev/null and "b/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..970fe6511
Binary files /dev/null and "b/ui/public/depot/\347\237\263\346\243\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\237\263\350\213\261\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\237\263\350\213\261\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6847d9e10
Binary files /dev/null and "b/ui/public/depot/\347\237\263\350\213\261\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\240\224\347\243\250\347\237\263.webp" "b/ui/public/depot/\347\240\224\347\243\250\347\237\263.webp"
new file mode 100644
index 000000000..9d08d7c35
Binary files /dev/null and "b/ui/public/depot/\347\240\224\347\243\250\347\237\263.webp" differ
diff --git "a/ui/public/depot/\347\240\264\346\215\237\350\243\205\347\275\256.webp" "b/ui/public/depot/\347\240\264\346\215\237\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..b5bc66485
Binary files /dev/null and "b/ui/public/depot/\347\240\264\346\215\237\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\347\240\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\240\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2dbcfda2c
Binary files /dev/null and "b/ui/public/depot/\347\240\276\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\240\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\240\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..320cba63d
Binary files /dev/null and "b/ui/public/depot/\347\240\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\242\263.webp" "b/ui/public/depot/\347\242\263.webp"
new file mode 100644
index 000000000..872914e1d
Binary files /dev/null and "b/ui/public/depot/\347\242\263.webp" differ
diff --git "a/ui/public/depot/\347\242\263\347\264\240.webp" "b/ui/public/depot/\347\242\263\347\264\240.webp"
new file mode 100644
index 000000000..948db8dbb
Binary files /dev/null and "b/ui/public/depot/\347\242\263\347\264\240.webp" differ
diff --git "a/ui/public/depot/\347\242\263\347\264\240\347\273\204.webp" "b/ui/public/depot/\347\242\263\347\264\240\347\273\204.webp"
new file mode 100644
index 000000000..b8fee7c39
Binary files /dev/null and "b/ui/public/depot/\347\242\263\347\264\240\347\273\204.webp" differ
diff --git "a/ui/public/depot/\347\250\200\351\237\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\250\200\351\237\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b7b15d128
Binary files /dev/null and "b/ui/public/depot/\347\250\200\351\237\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c13e14194
Binary files /dev/null and "b/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..89fb70a12
Binary files /dev/null and "b/ui/public/depot/\347\251\272\345\274\246\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\346\236\204\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\346\236\204\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1cd9fa4e1
Binary files /dev/null and "b/ui/public/depot/\347\251\272\346\236\204\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8d7b88e7e
Binary files /dev/null and "b/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9fb34c28f
Binary files /dev/null and "b/ui/public/depot/\347\251\272\347\210\206\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5e2c839c1
Binary files /dev/null and "b/ui/public/depot/\347\251\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\251\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\251\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..21ff3e7b4
Binary files /dev/null and "b/ui/public/depot/\347\251\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..09730a257
Binary files /dev/null and "b/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8a6e6ad6d
Binary files /dev/null and "b/ui/public/depot/\347\261\263\346\240\274\351\262\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\262\276\347\202\274\346\272\266\345\211\202.webp" "b/ui/public/depot/\347\262\276\347\202\274\346\272\266\345\211\202.webp"
new file mode 100644
index 000000000..feda7e9d3
Binary files /dev/null and "b/ui/public/depot/\347\262\276\347\202\274\346\272\266\345\211\202.webp" differ
diff --git "a/ui/public/depot/\347\263\226.webp" "b/ui/public/depot/\347\263\226.webp"
new file mode 100644
index 000000000..d0ff1a915
Binary files /dev/null and "b/ui/public/depot/\347\263\226.webp" differ
diff --git "a/ui/public/depot/\347\263\226\347\273\204.webp" "b/ui/public/depot/\347\263\226\347\273\204.webp"
new file mode 100644
index 000000000..7a18c09c7
Binary files /dev/null and "b/ui/public/depot/\347\263\226\347\273\204.webp" differ
diff --git "a/ui/public/depot/\347\263\226\350\201\232\345\235\227.webp" "b/ui/public/depot/\347\263\226\350\201\232\345\235\227.webp"
new file mode 100644
index 000000000..e8fbe6e29
Binary files /dev/null and "b/ui/public/depot/\347\263\226\350\201\232\345\235\227.webp" differ
diff --git "a/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b8d7ebef5
Binary files /dev/null and "b/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ddd877909
Binary files /dev/null and "b/ui/public/depot/\347\265\256\351\233\250\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f6b7d6eb2
Binary files /dev/null and "b/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7410c7e49
Binary files /dev/null and "b/ui/public/depot/\347\272\242\344\272\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4f962978f
Binary files /dev/null and "b/ui/public/depot/\347\272\242\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9f72712cf
Binary files /dev/null and "b/ui/public/depot/\347\272\242\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ff7ca0bbc
Binary files /dev/null and "b/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6b11e5557
Binary files /dev/null and "b/ui/public/depot/\347\272\242\350\261\206\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\242\351\232\274\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\242\351\232\274\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6e660e6cd
Binary files /dev/null and "b/ui/public/depot/\347\272\242\351\232\274\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..781155dc9
Binary files /dev/null and "b/ui/public/depot/\347\272\257\347\203\254\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\273\256\350\211\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\273\256\350\211\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9c5392a18
Binary files /dev/null and "b/ui/public/depot/\347\273\256\350\211\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\273\264\344\273\200\346\210\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\273\264\344\273\200\346\210\264\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..886c06df7
Binary files /dev/null and "b/ui/public/depot/\347\273\264\344\273\200\346\210\264\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e6a42630e
Binary files /dev/null and "b/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5a0441df2
Binary files /dev/null and "b/ui/public/depot/\347\273\264\350\215\273\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..094cb2e04
Binary files /dev/null and "b/ui/public/depot/\347\274\204\351\273\230\345\276\267\345\205\213\350\220\250\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..410f3d489
Binary files /dev/null and "b/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c28d1a355
Binary files /dev/null and "b/ui/public/depot/\347\274\240\344\270\270\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\274\252\345\260\224\350\265\233\346\200\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\274\252\345\260\224\350\265\233\346\200\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c7ff6acdb
Binary files /dev/null and "b/ui/public/depot/\347\274\252\345\260\224\350\265\233\346\200\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\256\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\275\227\345\256\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..595259352
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\256\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231II.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231II.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231II.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231III.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231III.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231III.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231IV.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231IV.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231IV.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231V.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231V.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231V.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VI.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VI.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VI.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VII.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VII.webp"
new file mode 100644
index 000000000..451840527
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\347\211\251\350\265\204\350\241\245\347\273\231VII.webp" differ
diff --git "a/ui/public/depot/\347\275\227\345\276\267\345\262\233\350\277\216\346\230\245\347\272\242\345\214\205.webp" "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\350\277\216\346\230\245\347\272\242\345\214\205.webp"
new file mode 100644
index 000000000..e0e69fba6
Binary files /dev/null and "b/ui/public/depot/\347\275\227\345\276\267\345\262\233\350\277\216\346\230\245\347\272\242\345\214\205.webp" differ
diff --git "a/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3c2634856
Binary files /dev/null and "b/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..06df8ca09
Binary files /dev/null and "b/ui/public/depot/\347\275\227\346\257\224\350\217\210\345\241\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\276\275\346\257\233\347\254\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\276\275\346\257\233\347\254\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e312431c1
Binary files /dev/null and "b/ui/public/depot/\347\276\275\346\257\233\347\254\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..827041ec6
Binary files /dev/null and "b/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0089aa1a5
Binary files /dev/null and "b/ui/public/depot/\347\277\216\347\276\275\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0703e5b70
Binary files /dev/null and "b/ui/public/depot/\350\200\200\351\252\221\345\243\253\344\270\264\345\205\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\200\201\351\262\244\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\200\201\351\262\244\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b24c63c74
Binary files /dev/null and "b/ui/public/depot/\350\200\201\351\262\244\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\200\266\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\200\266\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..55aeddc06
Binary files /dev/null and "b/ui/public/depot/\350\200\266\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\201\232\345\220\210\345\207\235\350\203\266.webp" "b/ui/public/depot/\350\201\232\345\220\210\345\207\235\350\203\266.webp"
new file mode 100644
index 000000000..a9bb37140
Binary files /dev/null and "b/ui/public/depot/\350\201\232\345\220\210\345\207\235\350\203\266.webp" differ
diff --git "a/ui/public/depot/\350\201\232\345\220\210\345\211\202.webp" "b/ui/public/depot/\350\201\232\345\220\210\345\211\202.webp"
new file mode 100644
index 000000000..b4d4cf943
Binary files /dev/null and "b/ui/public/depot/\350\201\232\345\220\210\345\211\202.webp" differ
diff --git "a/ui/public/depot/\350\201\232\351\205\270\351\205\257.webp" "b/ui/public/depot/\350\201\232\351\205\270\351\205\257.webp"
new file mode 100644
index 000000000..ed8af33d9
Binary files /dev/null and "b/ui/public/depot/\350\201\232\351\205\270\351\205\257.webp" differ
diff --git "a/ui/public/depot/\350\201\232\351\205\270\351\205\257\345\235\227.webp" "b/ui/public/depot/\350\201\232\351\205\270\351\205\257\345\235\227.webp"
new file mode 100644
index 000000000..3e67ce1ce
Binary files /dev/null and "b/ui/public/depot/\350\201\232\351\205\270\351\205\257\345\235\227.webp" differ
diff --git "a/ui/public/depot/\350\201\232\351\205\270\351\205\257\347\273\204.webp" "b/ui/public/depot/\350\201\232\351\205\270\351\205\257\347\273\204.webp"
new file mode 100644
index 000000000..e3cc28978
Binary files /dev/null and "b/ui/public/depot/\350\201\232\351\205\270\351\205\257\347\273\204.webp" differ
diff --git "a/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8e3754228
Binary files /dev/null and "b/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e9decbc1b
Binary files /dev/null and "b/ui/public/depot/\350\203\275\345\244\251\344\275\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\345\215\260\345\210\273\344\273\252.webp" "b/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\345\215\260\345\210\273\344\273\252.webp"
new file mode 100644
index 000000000..63077e3c8
Binary files /dev/null and "b/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\345\215\260\345\210\273\344\273\252.webp" differ
diff --git "a/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\347\273\204\345\215\260\345\210\273\344\273\252.webp" "b/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\347\273\204\345\215\260\345\210\273\344\273\252.webp"
new file mode 100644
index 000000000..06e2ea3c5
Binary files /dev/null and "b/ui/public/depot/\350\207\252\345\212\251\350\212\257\347\211\207\347\273\204\345\215\260\345\210\273\344\273\252.webp" differ
diff --git "a/ui/public/depot/\350\207\263\347\256\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\207\263\347\256\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..180557580
Binary files /dev/null and "b/ui/public/depot/\350\207\263\347\256\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\207\263\347\272\257\346\272\220\347\237\263.webp" "b/ui/public/depot/\350\207\263\347\272\257\346\272\220\347\237\263.webp"
new file mode 100644
index 000000000..019b86c37
Binary files /dev/null and "b/ui/public/depot/\350\207\263\347\272\257\346\272\220\347\237\263.webp" differ
diff --git "a/ui/public/depot/\350\211\276\344\270\235\351\273\233\345\260\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\211\276\344\270\235\351\273\233\345\260\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8c688c405
Binary files /dev/null and "b/ui/public/depot/\350\211\276\344\270\235\351\273\233\345\260\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\211\276\344\270\275\345\246\256\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\211\276\344\270\275\345\246\256\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..49c15163c
Binary files /dev/null and "b/ui/public/depot/\350\211\276\344\270\275\345\246\256\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\211\276\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\211\276\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8d4bca45d
Binary files /dev/null and "b/ui/public/depot/\350\211\276\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..84412d9cd
Binary files /dev/null and "b/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e525ccb16
Binary files /dev/null and "b/ui/public/depot/\350\211\276\351\233\205\346\263\225\346\213\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..97e677951
Binary files /dev/null and "b/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..95b053d58
Binary files /dev/null and "b/ui/public/depot/\350\212\231\345\205\260\345\215\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ba6b869c7
Binary files /dev/null and "b/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0fbfd2c69
Binary files /dev/null and "b/ui/public/depot/\350\212\231\350\223\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f68a4d3c7
Binary files /dev/null and "b/ui/public/depot/\350\212\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c26af2ad3
Binary files /dev/null and "b/ui/public/depot/\350\212\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\257\347\211\207\345\212\251\345\211\202.webp" "b/ui/public/depot/\350\212\257\347\211\207\345\212\251\345\211\202.webp"
new file mode 100644
index 000000000..c0d44da69
Binary files /dev/null and "b/ui/public/depot/\350\212\257\347\211\207\345\212\251\345\211\202.webp" differ
diff --git "a/ui/public/depot/\350\212\257\347\211\207\347\273\204\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp" "b/ui/public/depot/\350\212\257\347\211\207\347\273\204\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp"
new file mode 100644
index 000000000..2ff47a2f0
Binary files /dev/null and "b/ui/public/depot/\350\212\257\347\211\207\347\273\204\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp" differ
diff --git "a/ui/public/depot/\350\212\257\347\211\207\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp" "b/ui/public/depot/\350\212\257\347\211\207\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp"
new file mode 100644
index 000000000..d6e400b2b
Binary files /dev/null and "b/ui/public/depot/\350\212\257\347\211\207\350\207\252\345\212\251\346\211\223\345\215\260\347\233\222.webp" differ
diff --git "a/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..65ca3b9c1
Binary files /dev/null and "b/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a2e544a02
Binary files /dev/null and "b/ui/public/depot/\350\212\263\346\261\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..905affa3f
Binary files /dev/null and "b/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..295b87a2b
Binary files /dev/null and "b/ui/public/depot/\350\213\207\350\215\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\215\350\213\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\215\350\213\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..956db54e2
Binary files /dev/null and "b/ui/public/depot/\350\213\215\350\213\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bbab581e0
Binary files /dev/null and "b/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..239d11956
Binary files /dev/null and "b/ui/public/depot/\350\213\217\350\213\217\346\264\233\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\213\246\350\211\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\213\246\350\211\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7def8dc8c
Binary files /dev/null and "b/ui/public/depot/\350\213\246\350\211\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\215\222\350\212\234\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\350\215\222\350\212\234\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..fa6d16f8b
Binary files /dev/null and "b/ui/public/depot/\350\215\222\350\212\234\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\350\216\216\350\215\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\216\350\215\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6622d4c91
Binary files /dev/null and "b/ui/public/depot/\350\216\216\350\215\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c67a02aca
Binary files /dev/null and "b/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1499ddf8f
Binary files /dev/null and "b/ui/public/depot/\350\216\253\346\226\257\346\217\220\351\251\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\261\344\274\212\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\261\344\274\212\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..685ddd725
Binary files /dev/null and "b/ui/public/depot/\350\216\261\344\274\212\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5e77085af
Binary files /dev/null and "b/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..84aba4ce3
Binary files /dev/null and "b/ui/public/depot/\350\216\261\346\201\251\345\223\210\347\211\271\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\216\261\346\254\247\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\216\261\346\254\247\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..eb619b273
Binary files /dev/null and "b/ui/public/depot/\350\216\261\346\254\247\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\217\262\344\272\232\346\242\205\345\241\224\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\217\262\344\272\232\346\242\205\345\241\224\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8d987e5f0
Binary files /dev/null and "b/ui/public/depot/\350\217\262\344\272\232\346\242\205\345\241\224\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..35c714e12
Binary files /dev/null and "b/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..33b4bd626
Binary files /dev/null and "b/ui/public/depot/\350\223\235\346\257\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\226\204\347\273\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\226\204\347\273\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a4019a85f
Binary files /dev/null and "b/ui/public/depot/\350\226\204\347\273\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\226\207\350\226\207\345\256\211\345\250\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\226\207\350\226\207\345\256\211\345\250\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1238184c1
Binary files /dev/null and "b/ui/public/depot/\350\226\207\350\226\207\345\256\211\345\250\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\232\200\346\270\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\232\200\346\270\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3bd8d3591
Binary files /dev/null and "b/ui/public/depot/\350\232\200\346\270\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..aa39f5f65
Binary files /dev/null and "b/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cd9e2aa13
Binary files /dev/null and "b/ui/public/depot/\350\233\207\345\261\240\347\256\261\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\233\256\351\263\236\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\350\233\256\351\263\236\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..552635db3
Binary files /dev/null and "b/ui/public/depot/\350\233\256\351\263\236\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\350\234\234\350\216\223\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\234\234\350\216\223\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..07d4da9f5
Binary files /dev/null and "b/ui/public/depot/\350\234\234\350\216\223\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..db84d341d
Binary files /dev/null and "b/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..42945442e
Binary files /dev/null and "b/ui/public/depot/\350\234\234\350\234\241\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\241\241\346\262\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\241\241\346\262\231\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d4c116cc4
Binary files /dev/null and "b/ui/public/depot/\350\241\241\346\262\231\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\243\205\347\275\256.webp" "b/ui/public/depot/\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..bc02b6c14
Binary files /dev/null and "b/ui/public/depot/\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d24b88e07
Binary files /dev/null and "b/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..576166cd1
Binary files /dev/null and "b/ui/public/depot/\350\244\220\346\236\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\244\220\347\264\240\347\272\244\347\273\264.webp" "b/ui/public/depot/\350\244\220\347\264\240\347\272\244\347\273\264.webp"
new file mode 100644
index 000000000..f9228c9ee
Binary files /dev/null and "b/ui/public/depot/\350\244\220\347\264\240\347\272\244\347\273\264.webp" differ
diff --git "a/ui/public/depot/\350\247\201\350\241\214\350\200\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\247\201\350\241\214\350\200\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d7b6aeaf2
Binary files /dev/null and "b/ui/public/depot/\350\247\201\350\241\214\350\200\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..84f52a047
Binary files /dev/null and "b/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..02247fb2f
Binary files /dev/null and "b/ui/public/depot/\350\247\222\345\263\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\256\257\344\275\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\256\257\344\275\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..08956e4cc
Binary files /dev/null and "b/ui/public/depot/\350\256\257\344\275\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0c7a307ed
Binary files /dev/null and "b/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ee64a9d21
Binary files /dev/null and "b/ui/public/depot/\350\257\227\346\200\200\351\233\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..53a8437fe
Binary files /dev/null and "b/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d5e0566ff
Binary files /dev/null and "b/ui/public/depot/\350\260\203\351\246\231\345\270\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\260\234\345\233\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\260\234\345\233\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d8cc68c55
Binary files /dev/null and "b/ui/public/depot/\350\260\234\345\233\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4e052fb0d
Binary files /dev/null and "b/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..dc6aa4afa
Binary files /dev/null and "b/ui/public/depot/\350\261\206\350\213\227\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\264\235\345\250\234\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\264\235\345\250\234\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6f2f6dd21
Binary files /dev/null and "b/ui/public/depot/\350\264\235\345\250\234\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..64ec1003a
Binary files /dev/null and "b/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bb94b391a
Binary files /dev/null and "b/ui/public/depot/\350\264\276\347\273\264\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp"
new file mode 100644
index 000000000..497c57c09
Binary files /dev/null and "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" differ
diff --git "a/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp" "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..db69a30d8
Binary files /dev/null and "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp"
new file mode 100644
index 000000000..58f1ef0e5
Binary files /dev/null and "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" differ
diff --git "a/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp" "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp"
new file mode 100644
index 000000000..dbb919cad
Binary files /dev/null and "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp" differ
diff --git "a/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp" "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..c192bfd33
Binary files /dev/null and "b/ui/public/depot/\350\265\204\346\267\261\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\350\265\204\350\264\250\345\207\255\350\257\201.webp" "b/ui/public/depot/\350\265\204\350\264\250\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..6b818387b
Binary files /dev/null and "b/ui/public/depot/\350\265\204\350\264\250\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\350\265\235\346\263\242\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\350\265\235\346\263\242\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..d250d3ec9
Binary files /dev/null and "b/ui/public/depot/\350\265\235\346\263\242\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\350\265\244\345\206\254\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\244\345\206\254\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f0071bdee
Binary files /dev/null and "b/ui/public/depot/\350\265\244\345\206\254\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\244\351\207\221.webp" "b/ui/public/depot/\350\265\244\351\207\221.webp"
new file mode 100644
index 000000000..13b766c77
Binary files /dev/null and "b/ui/public/depot/\350\265\244\351\207\221.webp" differ
diff --git "a/ui/public/depot/\350\265\253\345\276\267\351\233\267\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\253\345\276\267\351\233\267\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d3cd6ec3c
Binary files /dev/null and "b/ui/public/depot/\350\265\253\345\276\267\351\233\267\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f5f3e5315
Binary files /dev/null and "b/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8880c8476
Binary files /dev/null and "b/ui/public/depot/\350\265\253\346\213\211\346\240\274\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e5556a3f8
Binary files /dev/null and "b/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d2a12d7cb
Binary files /dev/null and "b/ui/public/depot/\350\265\253\351\273\230\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\265\267\346\272\220\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\350\265\267\346\272\220\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..81bedcac1
Binary files /dev/null and "b/ui/public/depot/\350\265\267\346\272\220\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\350\267\203\350\267\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\267\203\350\267\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5e590063b
Binary files /dev/null and "b/ui/public/depot/\350\267\203\350\267\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\275\246\345\260\224\345\260\274\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\275\246\345\260\224\345\260\274\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..82c5f22cf
Binary files /dev/null and "b/ui/public/depot/\350\275\246\345\260\224\345\260\274\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\275\254\350\264\250\347\233\220\347\273\204.webp" "b/ui/public/depot/\350\275\254\350\264\250\347\233\220\347\273\204.webp"
new file mode 100644
index 000000000..a09d08f59
Binary files /dev/null and "b/ui/public/depot/\350\275\254\350\264\250\347\233\220\347\273\204.webp" differ
diff --git "a/ui/public/depot/\350\275\254\350\264\250\347\233\220\350\201\232\345\235\227.webp" "b/ui/public/depot/\350\275\254\350\264\250\347\233\220\350\201\232\345\235\227.webp"
new file mode 100644
index 000000000..a78ea511e
Binary files /dev/null and "b/ui/public/depot/\350\275\254\350\264\250\347\233\220\350\201\232\345\235\227.webp" differ
diff --git "a/ui/public/depot/\350\275\273\351\224\260\347\237\277.webp" "b/ui/public/depot/\350\275\273\351\224\260\347\237\277.webp"
new file mode 100644
index 000000000..a2383a561
Binary files /dev/null and "b/ui/public/depot/\350\275\273\351\224\260\347\237\277.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\276\205\345\212\251\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..89d43bf40
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..5e02322db
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..e5cf3ec59
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..383b2390c
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\350\276\205\345\212\251\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..e54abb8a7
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\276\205\345\212\251\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c0089b61e
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207.webp" "b/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..efa476a8e
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..ff10e7194
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\350\276\205\345\212\251\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\276\205\345\212\251\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b24df9a4b
Binary files /dev/null and "b/ui/public/depot/\350\276\205\345\212\251\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\276\276\346\240\274\350\276\276\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\276\276\346\240\274\350\276\276\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ac0feb7a9
Binary files /dev/null and "b/ui/public/depot/\350\276\276\346\240\274\350\276\276\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\221\345\215\253\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3f546394d
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..5ca198e52
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..03b420995
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..0ea4ba749
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\350\277\221\345\215\253\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..a80a72b36
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\221\345\215\253\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d1cf69426
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207.webp" "b/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..5c28acc7b
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..e38e61163
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\350\277\221\345\215\253\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\221\345\215\253\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..17648074c
Binary files /dev/null and "b/ui/public/depot/\350\277\221\345\215\253\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\233\351\230\266\345\212\240\345\233\272\345\273\272\346\235\220.webp" "b/ui/public/depot/\350\277\233\351\230\266\345\212\240\345\233\272\345\273\272\346\235\220.webp"
new file mode 100644
index 000000000..6fa6de629
Binary files /dev/null and "b/ui/public/depot/\350\277\233\351\230\266\345\212\240\345\233\272\345\273\272\346\235\220.webp" differ
diff --git "a/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..722afaf8f
Binary files /dev/null and "b/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..315a87468
Binary files /dev/null and "b/ui/public/depot/\350\277\234\345\261\261\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\234\347\211\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\234\347\211\231\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..83d30ed91
Binary files /dev/null and "b/ui/public/depot/\350\277\234\347\211\231\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\350\277\267\350\277\255\351\246\231\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\350\277\267\350\277\255\351\246\231\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..50a54cf8b
Binary files /dev/null and "b/ui/public/depot/\350\277\267\350\277\255\351\246\231\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1681602b7
Binary files /dev/null and "b/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d54199054
Binary files /dev/null and "b/ui/public/depot/\351\200\201\350\221\254\344\272\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\200\232\347\224\250\345\207\255\350\257\201.webp" "b/ui/public/depot/\351\200\232\347\224\250\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..fabea405f
Binary files /dev/null and "b/ui/public/depot/\351\200\232\347\224\250\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\351\200\273\345\220\204\346\226\257\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\200\273\345\220\204\346\226\257\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..32eb2e037
Binary files /dev/null and "b/ui/public/depot/\351\200\273\345\220\204\346\226\257\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\205\256\345\207\235\351\233\206.webp" "b/ui/public/depot/\351\205\256\345\207\235\351\233\206.webp"
new file mode 100644
index 000000000..a7a6cef70
Binary files /dev/null and "b/ui/public/depot/\351\205\256\345\207\235\351\233\206.webp" differ
diff --git "a/ui/public/depot/\351\205\256\345\207\235\351\233\206\347\273\204.webp" "b/ui/public/depot/\351\205\256\345\207\235\351\233\206\347\273\204.webp"
new file mode 100644
index 000000000..658ad52a3
Binary files /dev/null and "b/ui/public/depot/\351\205\256\345\207\235\351\233\206\347\273\204.webp" differ
diff --git "a/ui/public/depot/\351\205\256\351\230\265\345\210\227.webp" "b/ui/public/depot/\351\205\256\351\230\265\345\210\227.webp"
new file mode 100644
index 000000000..a9fe767e4
Binary files /dev/null and "b/ui/public/depot/\351\205\256\351\230\265\345\210\227.webp" differ
diff --git "a/ui/public/depot/\351\205\257\345\216\237\346\226\231.webp" "b/ui/public/depot/\351\205\257\345\216\237\346\226\231.webp"
new file mode 100644
index 000000000..7161a5c46
Binary files /dev/null and "b/ui/public/depot/\351\205\257\345\216\237\346\226\231.webp" differ
diff --git "a/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c10e507e4
Binary files /dev/null and "b/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..20604cf82
Binary files /dev/null and "b/ui/public/depot/\351\205\270\347\263\226\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\207\207\350\264\255\345\207\255\350\257\201.webp" "b/ui/public/depot/\351\207\207\350\264\255\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..43d57c453
Binary files /dev/null and "b/ui/public/depot/\351\207\207\350\264\255\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\351\207\215\345\262\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\207\215\345\262\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0d1e01faf
Binary files /dev/null and "b/ui/public/depot/\351\207\215\345\262\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\344\274\240\346\211\277\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\207\215\350\243\205\344\274\240\346\211\277\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5a96e3769
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\344\274\240\346\211\277\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\216\237\344\273\266.webp" "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\216\237\344\273\266.webp"
new file mode 100644
index 000000000..c9ed18a8e
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\216\237\344\273\266.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp"
new file mode 100644
index 000000000..88c73ecca
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\345\244\215\345\210\266\345\223\201.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\350\227\217\345\223\201.webp" "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\350\227\217\345\223\201.webp"
new file mode 100644
index 000000000..fd38cbd86
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\344\277\241\347\211\251\350\227\217\345\223\201.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\345\217\214\350\212\257\347\211\207.webp" "b/ui/public/depot/\351\207\215\350\243\205\345\217\214\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..01787b02b
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\345\217\214\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\347\232\207\345\256\266\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\207\215\350\243\205\347\232\207\345\256\266\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ab5785d7f
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\347\232\207\345\256\266\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207.webp" "b/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207.webp"
new file mode 100644
index 000000000..a1c9d21f2
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207\347\273\204.webp" "b/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207\347\273\204.webp"
new file mode 100644
index 000000000..d380f0634
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\350\212\257\347\211\207\347\273\204.webp" differ
diff --git "a/ui/public/depot/\351\207\215\350\243\205\351\201\227\344\272\247\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\207\215\350\243\205\351\201\227\344\272\247\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d892c7b43
Binary files /dev/null and "b/ui/public/depot/\351\207\215\350\243\205\351\201\227\344\272\247\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\207\216\351\254\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\207\216\351\254\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4cdabb856
Binary files /dev/null and "b/ui/public/depot/\351\207\216\351\254\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3bdc8c6cf
Binary files /dev/null and "b/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ed99e3690
Binary files /dev/null and "b/ui/public/depot/\351\223\203\345\205\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\205\345\260\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\351\223\205\345\260\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..0bad679fb
Binary files /dev/null and "b/ui/public/depot/\351\223\205\345\260\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..550d35bb6
Binary files /dev/null and "b/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..59c174dee
Binary files /dev/null and "b/ui/public/depot/\351\223\205\350\270\235\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\216\351\223\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\216\351\223\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bd34f2260
Binary files /dev/null and "b/ui/public/depot/\351\223\216\351\223\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..880c7a499
Binary files /dev/null and "b/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..97348c061
Binary files /dev/null and "b/ui/public/depot/\351\223\266\347\201\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\223\270\351\223\201\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\223\270\351\223\201\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..908ce34f2
Binary files /dev/null and "b/ui/public/depot/\351\223\270\351\223\201\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\224\217\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\224\217\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..88726f63f
Binary files /dev/null and "b/ui/public/depot/\351\224\217\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\224\241\344\272\272\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\224\241\344\272\272\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..64aca21d6
Binary files /dev/null and "b/ui/public/depot/\351\224\241\344\272\272\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\224\241\345\205\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\224\241\345\205\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b6daabc24
Binary files /dev/null and "b/ui/public/depot/\351\224\241\345\205\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\227\252\345\207\273\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\227\252\345\207\273\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d527699d4
Binary files /dev/null and "b/ui/public/depot/\351\227\252\345\207\273\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..948a9abc7
Binary files /dev/null and "b/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..00a3ea7e7
Binary files /dev/null and "b/ui/public/depot/\351\227\252\347\201\265\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\346\226\257\345\215\241\347\272\266\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\346\226\257\345\215\241\347\272\266\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cc02ab9dd
Binary files /dev/null and "b/ui/public/depot/\351\230\277\346\226\257\345\215\241\347\272\266\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..87734597d
Binary files /dev/null and "b/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0f45c35bf
Binary files /dev/null and "b/ui/public/depot/\351\230\277\346\266\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0ed030c4d
Binary files /dev/null and "b/ui/public/depot/\351\230\277\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5af7681f1
Binary files /dev/null and "b/ui/public/depot/\351\230\277\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\347\261\263\345\250\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\347\261\263\345\250\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..bba4b277f
Binary files /dev/null and "b/ui/public/depot/\351\230\277\347\261\263\345\250\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\230\277\347\275\227\347\216\233\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\230\277\347\275\227\347\216\233\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a4aaa2f59
Binary files /dev/null and "b/ui/public/depot/\351\230\277\347\275\227\347\216\233\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\231\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\231\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..d59e8a8ad
Binary files /dev/null and "b/ui/public/depot/\351\231\210\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\231\210\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\231\210\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3f69f4d70
Binary files /dev/null and "b/ui/public/depot/\351\231\210\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b9d2b5b81
Binary files /dev/null and "b/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f9cf7018a
Binary files /dev/null and "b/ui/public/depot/\351\231\250\346\230\237\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\232\220\347\216\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\232\220\347\216\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..53c3956b5
Binary files /dev/null and "b/ui/public/depot/\351\232\220\347\216\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\233\252\347\273\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\233\252\347\273\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..6ba423bf5
Binary files /dev/null and "b/ui/public/depot/\351\233\252\347\273\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\233\252\351\233\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\233\252\351\233\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..136cb22b1
Binary files /dev/null and "b/ui/public/depot/\351\233\252\351\233\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..8e72285c0
Binary files /dev/null and "b/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..1c1c97b59
Binary files /dev/null and "b/ui/public/depot/\351\233\267\350\233\207\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\234\215\345\260\224\346\265\267\351\233\205\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\234\215\345\260\224\346\265\267\351\233\205\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a53dfcfcf
Binary files /dev/null and "b/ui/public/depot/\351\234\215\345\260\224\346\265\267\351\233\205\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\234\234\345\215\216\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\234\234\345\215\216\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c88afa84b
Binary files /dev/null and "b/ui/public/depot/\351\234\234\345\215\216\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..fda5e0c01
Binary files /dev/null and "b/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..183205858
Binary files /dev/null and "b/ui/public/depot/\351\234\234\345\217\266\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\234\262\346\211\230\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\234\262\346\211\230\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c75e798b2
Binary files /dev/null and "b/ui/public/depot/\351\234\262\346\211\230\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\235\222\346\236\263\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\235\222\346\236\263\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4d98ca1db
Binary files /dev/null and "b/ui/public/depot/\351\235\222\346\236\263\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\236\255\345\210\203\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\236\255\345\210\203\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9478f8cf7
Binary files /dev/null and "b/ui/public/depot/\351\236\255\345\210\203\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\243\216\344\270\270\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\243\216\344\270\270\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7656b68cb
Binary files /dev/null and "b/ui/public/depot/\351\243\216\344\270\270\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..0bdd15655
Binary files /dev/null and "b/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..ecc6983fe
Binary files /dev/null and "b/ui/public/depot/\351\243\216\347\254\233\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..cd7dcb305
Binary files /dev/null and "b/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..2f928d2a7
Binary files /dev/null and "b/ui/public/depot/\351\243\237\351\223\201\345\205\275\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..e653df67c
Binary files /dev/null and "b/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7b4d40cc7
Binary files /dev/null and "b/ui/public/depot/\351\246\231\350\215\211\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" "b/ui/public/depot/\351\253\230\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp"
new file mode 100644
index 000000000..688477588
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\345\207\255\350\257\201.webp" "b/ui/public/depot/\351\253\230\347\272\247\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..a7bb0e893
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\345\212\240\345\233\272\345\273\272\346\235\220.webp" "b/ui/public/depot/\351\253\230\347\272\247\345\212\240\345\233\272\345\273\272\346\235\220.webp"
new file mode 100644
index 000000000..8f35a114d
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\345\212\240\345\233\272\345\273\272\346\235\220.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp" "b/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp"
new file mode 100644
index 000000000..1191c7290
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201-\346\226\260\344\272\272.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp" "b/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp"
new file mode 100644
index 000000000..04e5e2836
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\345\271\262\345\221\230\350\260\203\347\224\250\345\207\255\350\257\201.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp" "b/ui/public/depot/\351\253\230\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp"
new file mode 100644
index 000000000..587b0a6a7
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\346\235\220\346\226\231\346\217\220\350\264\247\345\210\270.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp"
new file mode 100644
index 000000000..610bfef03
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\346\212\200\345\267\247\351\233\206.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp" "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp"
new file mode 100644
index 000000000..407597668
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\350\243\205\347\275\256.webp" differ
diff --git "a/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp"
new file mode 100644
index 000000000..a09fad547
Binary files /dev/null and "b/ui/public/depot/\351\253\230\347\272\247\350\265\204\346\267\261\345\271\262\345\221\230\347\211\271\350\256\255\351\202\200\350\257\267\345\207\275.webp" differ
diff --git "a/ui/public/depot/\351\255\224\347\216\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\255\224\347\216\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a14b2831e
Binary files /dev/null and "b/ui/public/depot/\351\255\224\347\216\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\270\277\351\233\252\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\270\277\351\233\252\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..9aa0266c3
Binary files /dev/null and "b/ui/public/depot/\351\270\277\351\233\252\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\272\222\351\272\237R\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\272\222\351\272\237R\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..41e7ea355
Binary files /dev/null and "b/ui/public/depot/\351\272\222\351\272\237R\345\244\234\345\210\200\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..5f60e7328
Binary files /dev/null and "b/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..3f788814c
Binary files /dev/null and "b/ui/public/depot/\351\272\246\345\223\262\344\274\246\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\273\204\351\223\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" "b/ui/public/depot/\351\273\204\351\223\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp"
new file mode 100644
index 000000000..9d349a528
Binary files /dev/null and "b/ui/public/depot/\351\273\204\351\223\201\350\241\214\345\212\250\347\211\251\350\265\204\350\241\245\347\273\231.webp" differ
diff --git "a/ui/public/depot/\351\273\215\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\273\215\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..b0b1b1a1a
Binary files /dev/null and "b/ui/public/depot/\351\273\215\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\273\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\273\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..7e69204d8
Binary files /dev/null and "b/ui/public/depot/\351\273\221\347\232\204\344\270\255\345\235\232\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\273\221\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\273\221\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..88a3e944c
Binary files /dev/null and "b/ui/public/depot/\351\273\221\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..4ce60ae09
Binary files /dev/null and "b/ui/public/depot/\351\273\221\350\247\222\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\273\221\351\224\256\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\273\221\351\224\256\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..f564d2e65
Binary files /dev/null and "b/ui/public/depot/\351\273\221\351\224\256\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\275\220\345\260\224\346\237\245\345\205\213\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\275\220\345\260\224\346\237\245\345\205\213\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..c898cf898
Binary files /dev/null and "b/ui/public/depot/\351\275\220\345\260\224\346\237\245\345\205\213\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\276\231\350\210\214\345\205\260\347\232\204\344\277\241\347\211\251.webp" "b/ui/public/depot/\351\276\231\350\210\214\345\205\260\347\232\204\344\277\241\347\211\251.webp"
new file mode 100644
index 000000000..a1fa5f6f0
Binary files /dev/null and "b/ui/public/depot/\351\276\231\350\210\214\345\205\260\347\232\204\344\277\241\347\211\251.webp" differ
diff --git "a/ui/public/depot/\351\276\231\351\227\250\345\270\201.webp" "b/ui/public/depot/\351\276\231\351\227\250\345\270\201.webp"
new file mode 100644
index 000000000..8b6e2cb4e
Binary files /dev/null and "b/ui/public/depot/\351\276\231\351\227\250\345\270\201.webp" differ
diff --git "a/ui/public/depot/\351\276\231\351\252\250.webp" "b/ui/public/depot/\351\276\231\351\252\250.webp"
new file mode 100644
index 000000000..2d1e38faa
Binary files /dev/null and "b/ui/public/depot/\351\276\231\351\252\250.webp" differ
diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico
new file mode 100644
index 000000000..c36992b66
Binary files /dev/null and b/ui/public/favicon.ico differ
diff --git a/ui/public/help/sss-type.png b/ui/public/help/sss-type.png
new file mode 100644
index 000000000..fd23065ee
Binary files /dev/null and b/ui/public/help/sss-type.png differ
diff --git a/ui/public/map-OF-1.webp b/ui/public/map-OF-1.webp
new file mode 100644
index 000000000..99a972e75
Binary files /dev/null and b/ui/public/map-OF-1.webp differ
diff --git a/ui/public/product/exp3.png b/ui/public/product/exp3.png
new file mode 100644
index 000000000..4bec8360f
Binary files /dev/null and b/ui/public/product/exp3.png differ
diff --git a/ui/public/product/gold.png b/ui/public/product/gold.png
new file mode 100644
index 000000000..1ea248053
Binary files /dev/null and b/ui/public/product/gold.png differ
diff --git a/ui/public/product/lmd.png b/ui/public/product/lmd.png
new file mode 100644
index 000000000..2002cb312
Binary files /dev/null and b/ui/public/product/lmd.png differ
diff --git a/ui/public/product/orirock.png b/ui/public/product/orirock.png
new file mode 100644
index 000000000..a0a52372a
Binary files /dev/null and b/ui/public/product/orirock.png differ
diff --git a/ui/public/product/orundum.png b/ui/public/product/orundum.png
new file mode 100644
index 000000000..943f7da36
Binary files /dev/null and b/ui/public/product/orundum.png differ
diff --git "a/ui/public/shop/\344\273\243\347\263\226.png" "b/ui/public/shop/\344\273\243\347\263\226.png"
new file mode 100644
index 000000000..0fe918a4a
Binary files /dev/null and "b/ui/public/shop/\344\273\243\347\263\226.png" differ
diff --git "a/ui/public/shop/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.png" "b/ui/public/shop/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.png"
new file mode 100644
index 000000000..7eec13a4b
Binary files /dev/null and "b/ui/public/shop/\345\210\235\347\272\247\344\275\234\346\210\230\350\256\260\345\275\225.png" differ
diff --git "a/ui/public/shop/\345\212\240\346\200\245\350\256\270\345\217\257.png" "b/ui/public/shop/\345\212\240\346\200\245\350\256\270\345\217\257.png"
new file mode 100644
index 000000000..0dce110fb
Binary files /dev/null and "b/ui/public/shop/\345\212\240\346\200\245\350\256\270\345\217\257.png" differ
diff --git "a/ui/public/shop/\345\217\214\351\205\256.png" "b/ui/public/shop/\345\217\214\351\205\256.png"
new file mode 100644
index 000000000..e9517474f
Binary files /dev/null and "b/ui/public/shop/\345\217\214\351\205\256.png" differ
diff --git "a/ui/public/shop/\345\233\272\346\272\220\345\262\251.png" "b/ui/public/shop/\345\233\272\346\272\220\345\262\251.png"
new file mode 100644
index 000000000..437f9db7f
Binary files /dev/null and "b/ui/public/shop/\345\233\272\346\272\220\345\262\251.png" differ
diff --git "a/ui/public/shop/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.png" "b/ui/public/shop/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.png"
new file mode 100644
index 000000000..bd1069c55
Binary files /dev/null and "b/ui/public/shop/\345\237\272\347\241\200\344\275\234\346\210\230\350\256\260\345\275\225.png" differ
diff --git "a/ui/public/shop/\345\256\266\345\205\267\351\233\266\344\273\266.png" "b/ui/public/shop/\345\256\266\345\205\267\351\233\266\344\273\266.png"
new file mode 100644
index 000000000..6c4def4ed
Binary files /dev/null and "b/ui/public/shop/\345\256\266\345\205\267\351\233\266\344\273\266.png" differ
diff --git "a/ui/public/shop/\345\274\202\351\223\201.png" "b/ui/public/shop/\345\274\202\351\223\201.png"
new file mode 100644
index 000000000..f41594b06
Binary files /dev/null and "b/ui/public/shop/\345\274\202\351\223\201.png" differ
diff --git "a/ui/public/shop/\345\274\202\351\223\201\347\242\216\347\211\207.png" "b/ui/public/shop/\345\274\202\351\223\201\347\242\216\347\211\207.png"
new file mode 100644
index 000000000..13a285ce3
Binary files /dev/null and "b/ui/public/shop/\345\274\202\351\223\201\347\242\216\347\211\207.png" differ
diff --git "a/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.png" "b/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.png"
new file mode 100644
index 000000000..a5303d810
Binary files /dev/null and "b/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2671.png" differ
diff --git "a/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.png" "b/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.png"
new file mode 100644
index 000000000..762dd6d04
Binary files /dev/null and "b/ui/public/shop/\346\212\200\345\267\247\346\246\202\350\246\201\302\267\345\215\2672.png" differ
diff --git "a/ui/public/shop/\346\213\233\350\201\230\350\256\270\345\217\257.png" "b/ui/public/shop/\346\213\233\350\201\230\350\256\270\345\217\257.png"
new file mode 100644
index 000000000..595590600
Binary files /dev/null and "b/ui/public/shop/\346\213\233\350\201\230\350\256\270\345\217\257.png" differ
diff --git "a/ui/public/shop/\346\272\220\345\262\251.png" "b/ui/public/shop/\346\272\220\345\262\251.png"
new file mode 100644
index 000000000..6c9b599ed
Binary files /dev/null and "b/ui/public/shop/\346\272\220\345\262\251.png" differ
diff --git "a/ui/public/shop/\347\240\264\346\215\237\350\243\205\347\275\256.png" "b/ui/public/shop/\347\240\264\346\215\237\350\243\205\347\275\256.png"
new file mode 100644
index 000000000..58949601f
Binary files /dev/null and "b/ui/public/shop/\347\240\264\346\215\237\350\243\205\347\275\256.png" differ
diff --git "a/ui/public/shop/\347\242\263.png" "b/ui/public/shop/\347\242\263.png"
new file mode 100644
index 000000000..10e85d66c
Binary files /dev/null and "b/ui/public/shop/\347\242\263.png" differ
diff --git "a/ui/public/shop/\347\242\263\347\264\240.png" "b/ui/public/shop/\347\242\263\347\264\240.png"
new file mode 100644
index 000000000..7f441fbed
Binary files /dev/null and "b/ui/public/shop/\347\242\263\347\264\240.png" differ
diff --git "a/ui/public/shop/\347\263\226.png" "b/ui/public/shop/\347\263\226.png"
new file mode 100644
index 000000000..61e21867f
Binary files /dev/null and "b/ui/public/shop/\347\263\226.png" differ
diff --git "a/ui/public/shop/\350\201\232\351\205\270\351\205\257.png" "b/ui/public/shop/\350\201\232\351\205\270\351\205\257.png"
new file mode 100644
index 000000000..f947e0449
Binary files /dev/null and "b/ui/public/shop/\350\201\232\351\205\270\351\205\257.png" differ
diff --git "a/ui/public/shop/\350\243\205\347\275\256.png" "b/ui/public/shop/\350\243\205\347\275\256.png"
new file mode 100644
index 000000000..08b7ff50a
Binary files /dev/null and "b/ui/public/shop/\350\243\205\347\275\256.png" differ
diff --git "a/ui/public/shop/\350\265\244\351\207\221.png" "b/ui/public/shop/\350\265\244\351\207\221.png"
new file mode 100644
index 000000000..36a227e2f
Binary files /dev/null and "b/ui/public/shop/\350\265\244\351\207\221.png" differ
diff --git "a/ui/public/shop/\351\205\256\345\207\235\351\233\206.png" "b/ui/public/shop/\351\205\256\345\207\235\351\233\206.png"
new file mode 100644
index 000000000..e5a5f4a4c
Binary files /dev/null and "b/ui/public/shop/\351\205\256\345\207\235\351\233\206.png" differ
diff --git "a/ui/public/shop/\351\205\257\345\216\237\346\226\231.png" "b/ui/public/shop/\351\205\257\345\216\237\346\226\231.png"
new file mode 100644
index 000000000..568768a83
Binary files /dev/null and "b/ui/public/shop/\351\205\257\345\216\237\346\226\231.png" differ
diff --git "a/ui/public/shop/\351\276\231\351\227\250\345\270\201.png" "b/ui/public/shop/\351\276\231\351\227\250\345\270\201.png"
new file mode 100644
index 000000000..ec8756ee7
Binary files /dev/null and "b/ui/public/shop/\351\276\231\351\227\250\345\270\201.png" differ
diff --git a/ui/src/App.vue b/ui/src/App.vue
new file mode 100644
index 000000000..b9051ec22
--- /dev/null
+++ b/ui/src/App.vue
@@ -0,0 +1,533 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 日志
+
+
+
+
+
+ 设置
+
+
+
+
+
+ 排班
+
+
+
+
+
+ 报表
+
+
+
+
+
+ 心情曲线
+
+
+
+
+ 心情饼图
+
+
+
+
+ 仓库
+
+
+
+
+ 基建报告
+
+
+
+
+
+
+
+
+ 帮助
+
+
+
+
+
+ 资源
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/Clue.vue b/ui/src/components/Clue.vue
new file mode 100644
index 000000000..3134d8aa0
--- /dev/null
+++ b/ui/src/components/Clue.vue
@@ -0,0 +1,222 @@
+
+
+
+
+
+ 线索收集
+
+ 雷锋模式
+
+ 开启时,向好友赠送多余的线索;
+ 关闭则超过9个线索才送好友。
+
+
+
+
+
+
+
+ 信用作战
+
+
+ 借助战打OF-1
+ 使用指定编队中的指定干员
+
+
+
+
+
+
+
+
+
+ X
+
+ Y
+
+
+ {{ show_map ? '隐藏' : '显示' }}OF-1地图
+
+
+
+
+
+
+
+ 信用商店购物
+
+
+ 性价比参考:
+
+ 罗德岛物价局
+
+ 注意:跑单时赤金与作战记录均大幅升值
+
+
+
+
+
+ 停止购买
+ 无视黑名单继续购买,直至不再溢出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/DailyMission.vue b/ui/src/components/DailyMission.vue
new file mode 100644
index 000000000..603586e8b
--- /dev/null
+++ b/ui/src/components/DailyMission.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+ 森空岛签到
+
+
+
+
+ 森空岛账号:{{ account_info.account }}
+
+
+ 官服签到
+
+
+ B服签到
+
+
+
+
+
+ 领取邮件
+
+
+
+ 访问好友
+
+
+
+
+ 读取基报
+
+
+
+
+
+ 签到活动
+
+ 游戏内签到、矿区、限定池每日单抽等
+
+
+
+
+
+
diff --git a/ui/src/components/Depotswitch.vue b/ui/src/components/Depotswitch.vue
new file mode 100644
index 000000000..45af8c60e
--- /dev/null
+++ b/ui/src/components/Depotswitch.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+ 仓库物品混合读取
+
+ 请调整森空岛账号顺序,仅读取第一个账户指定服务器的材料
+
+
+
+
森空岛账号:{{ account_info.account }}
+
+
+ 官服
+ B服
+
+
+
+
+
+
diff --git a/ui/src/components/DropDown.vue b/ui/src/components/DropDown.vue
new file mode 100644
index 000000000..dbbcd3956
--- /dev/null
+++ b/ui/src/components/DropDown.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/Email.vue b/ui/src/components/Email.vue
new file mode 100644
index 000000000..282a7bac2
--- /dev/null
+++ b/ui/src/components/Email.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+ 邮件提醒
+
+
+ {{ custom_smtp_server.enable ? '自定义邮箱' : 'QQ邮箱' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账号
+ QQ邮箱
+
+
+
+
+
+ 密码
+
+ 授权码
+
+
+ https://service.mail.qq.com/detail/0/75
+
+
+
+
+
+
+
+
+
+
+
+ 标题前缀
+ 可用于区分来自多个Mower的邮件
+
+
+
+
+
+ 收件人
+ 不填时将邮件发给自己
+
+
+
+
+
+
+
发送测试邮件
+
{{ test_result }}
+
+
+
+
+
+
diff --git a/ui/src/components/HelpText.vue b/ui/src/components/HelpText.vue
new file mode 100644
index 000000000..ebd733740
--- /dev/null
+++ b/ui/src/components/HelpText.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/LongTasks.vue b/ui/src/components/LongTasks.vue
new file mode 100644
index 000000000..8a1fc604a
--- /dev/null
+++ b/ui/src/components/LongTasks.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ 大型任务
+
+
+ 开始与结束时间设置为相同值时全天开启。
+ 若结束时间早于开始时间,则表示开启至次日。例如:
+
+ - 23:00开始、8:00结束:表示从23:00至次日8:00执行大型任务;
+ - 10:00开始、14:00结束:表示从10:00至当日14:00执行大型任务。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/MaaBasic.vue b/ui/src/components/MaaBasic.vue
new file mode 100644
index 000000000..a1731fae5
--- /dev/null
+++ b/ui/src/components/MaaBasic.vue
@@ -0,0 +1,84 @@
+
+
+
+
+ Maa设置集成战略、保全派驻
+
+
+
+ ...
+
+
+
+ 刷新
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/MaaRogue.vue b/ui/src/components/MaaRogue.vue
new file mode 100644
index 000000000..6b148541f
--- /dev/null
+++ b/ui/src/components/MaaRogue.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 开局干员使用助战
+
+
+ 开局干员使用非好友助战
+
+
+
+
+
+ 刷新商店(指路鳞)
+
+
+
+
+
+
diff --git a/ui/src/components/MaaSss.vue b/ui/src/components/MaaSss.vue
new file mode 100644
index 000000000..a6fa1449e
--- /dev/null
+++ b/ui/src/components/MaaSss.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+ 关卡
+
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+
+
+ 下载
+
+ 取消
+
+
+ 输入“神秘代码”
+
+ 选择作业文件
+
+
+
+
+
+
+
+
{{ sss_data.title }}
+
{{ sss_data.details }}
+
+ 未选择作业
+
+
+ {{ o.name }}({{ o.skill }}技能)
+
+
+
+
+
+
+
diff --git a/ui/src/components/MaaWeekly.vue b/ui/src/components/MaaWeekly.vue
new file mode 100644
index 000000000..1ffd26bfe
--- /dev/null
+++ b/ui/src/components/MaaWeekly.vue
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+ 刷理智周计划
+
+
+ 支持的常驻关卡:
+
+ - 第一章、第八章、第十二章主线;
+ - 全部资源收集关卡。
+
+
+
+ PRTS.wiki:关卡一览/资源收集
+
+
+
+
+
+
+ 自动使用将要过期(约3天)的理智药
+
+
+ 仅在周末使用
+
+
+
+
+
+
+
+ {{ plan.weekday }} |
+
+
+ |
+
+
+
+
+
+
+
diff --git a/ui/src/components/MaaWeeklyNew.vue b/ui/src/components/MaaWeeklyNew.vue
new file mode 100644
index 000000000..e73f26f00
--- /dev/null
+++ b/ui/src/components/MaaWeeklyNew.vue
@@ -0,0 +1,523 @@
+
+
+
+
+ 刷理智周计划·新
+
+ 有问题先看问号,2023年12月6日更新了 请点击下方按钮
+
+ 先看上一个周计划问号
+ 如何不打本:把所有的"打"都点了或者点一次清除当前配置以匹配最新表格
+ 将要过期的理智药选项在上面 往上滑。
+ 建议每次更新后 点一次清除当前配置以匹配最新表格
+ 如果觉得这个表有什么要改进的 at群管理
+ 前三行的空行用来写 一些自定义关卡
+
+ 新用法SSReopen-XX 如SSReopen-FC可以刷照我以火的所有复刻本(FC-1到FC-8)
+
+
+ -
+ 在第一行填入HE-7
+
+ 第二行填入1-7
+
+ 则会刷活动关HE-7,若活动未开放,则刷1-7。
+
+ - 12-17标准 表示12-17标准难度。
+ - 12-17磨难 表示12-17磨难难度。
+ - 当期剿灭:输入“当期剿灭”后回车,生成 当期剿灭 标签。
+ -
+ 信用作战:若信用作战选项已开启,且当日计划不包含
+ 上次作战,则自动进行信用作战。
+
+
+
+
+ 清除当前配置以匹配最新表格
+
+
+
+
+ 全选 |
+ 关卡 |
+
+ {{ day[1] }}{{ currentDay === (index + 1) % 7 ? ' (今天)' : '' }}
+ |
+
+
+
+
+
+
+ togglePlan(plan)"
+ quaternary
+ style="width: 100%; height: 100%"
+ class="class1"
+ >
+ |
+
+ changestage(plan, value)"
+ />
+ {{ showstage(plan.stage) }}
+ |
+
+
+ togglePlanAndStage(plan, day)"
+ quaternary
+ style="width: 100%; height: 100%"
+ >
+ 打
+
+
+
+ |
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/PlanEditor.vue b/ui/src/components/PlanEditor.vue
new file mode 100644
index 000000000..9075ae9fa
--- /dev/null
+++ b/ui/src/components/PlanEditor.vue
@@ -0,0 +1,811 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ plan[r].name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
控制中枢
+
+
+
+
+
+
+
+
+
+
宿舍1
+
+
+
+
+
+
+
+
+
+
宿舍2
+
+
+
+
+
+
+
+
+
+
宿舍3
+
+
+
+
+
+
+
+
+
+
宿舍4
+
+
+
+
+
+
+
+
+
+
+
+
+
会客室
+
+
+
+
+
+
+
+
+
+
加工站
+
+
+
+
+
+
+
+
+
+
办公室
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 设施类别: |
+
+
+ {{ right_side_facility_name }}
+ |
+
+ 产物切产物功能暂未实装 |
+
+
+ |
+
+
+
+ 此宿舍内空位填充Free
+
+ |
+
+
+ 清空此设施内干员
+
+ |
+
+
+
+
+
+
+
+ 协助位
+ 训练位
+ 干员:
+ |
+
+
+ |
+
+ 组
+ 可以将有联动基建技能的干员或者心情掉率相等的干员编入同组
+ |
+
+
+ |
+ 替换: |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/ReclamationAlgorithm.vue b/ui/src/components/ReclamationAlgorithm.vue
new file mode 100644
index 000000000..332321a5d
--- /dev/null
+++ b/ui/src/components/ReclamationAlgorithm.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ 秒
+
+
+
+
diff --git a/ui/src/components/Recruit.vue b/ui/src/components/Recruit.vue
new file mode 100644
index 000000000..748320e8b
--- /dev/null
+++ b/ui/src/components/Recruit.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+ 公开招募
+
+
+
+
+
+ 启动间隔
+ 可填小数
+
+
+ 小时
+
+
+
+
+ 三星招募阈值
+ 剩余公招券大于此阈值时招募三星干员
+
+
+ 张
+
+
+
+
+
+ 自动选择
+ 手动选择
+
+
+
+
+ 五星词条组合唯一时自动选择
+
+
+ 保留支援机械标签
+
+
+
+
+
+
diff --git a/ui/src/components/SKLand.vue b/ui/src/components/SKLand.vue
new file mode 100644
index 000000000..80dadae9a
--- /dev/null
+++ b/ui/src/components/SKLand.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+ 森空岛账号
+
+ 连接失败时,请尝试:
+
+ - 同步系统时间后再试;
+ - 检查账号密码是否正确;
+ - 关闭代理软件或设置分流规则;
+ - 登录森空岛App,查看是否需要人机验证。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/SecretFront.vue b/ui/src/components/SecretFront.vue
new file mode 100644
index 000000000..6fc9c84eb
--- /dev/null
+++ b/ui/src/components/SecretFront.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ 秒
+
+
+
+
+
+
+
diff --git a/ui/src/components/SlickOperatorSelect.vue b/ui/src/components/SlickOperatorSelect.vue
new file mode 100644
index 000000000..d09b274ab
--- /dev/null
+++ b/ui/src/components/SlickOperatorSelect.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/TaskDialog.vue b/ui/src/components/TaskDialog.vue
new file mode 100644
index 000000000..1fd08d658
--- /dev/null
+++ b/ui/src/components/TaskDialog.vue
@@ -0,0 +1,367 @@
+
+
+
+
+
+
+
+
+
+
+ 不支持阿斯卡纶
+
+ 训练速度需手动输入,可使用
+ 明日方舟工具箱 查询
+
+ 任务开启前,请手动把专精干员放入训练室(Mower暂时不支持训练室换人)
+ 排班表是要填写协助位和训练位的,最好写从来没用的工具人。
+ 训练室排班表纠错暂时关闭,有需要纠错的朋友,请绑大组
+ 自动计算时暂时默认2,3专精获得小鸟/狗剩增益效果
+ 如果开启专精时未获得减半增益,本次专精可手动计算时间以添加艾丽妮替换任务
+
+ 参考攻略:
+
+ 通用最速专精方案
+
+
+
+
+ 任务
+
+
+
+ 添加任务
+
+
+
+
+ 会客室:meeting
+ 办公室:contact
+ 加工站:factory
+ 训练室:train
+ 控制中枢:central
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加专精工具人
+
+
+
+
+
+
+
+
+
+ %
+
+
+
+ 中途换人
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/TriggerDialog.vue b/ui/src/components/TriggerDialog.vue
new file mode 100644
index 000000000..21bfadb67
--- /dev/null
+++ b/ui/src/components/TriggerDialog.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/TriggerEditor.vue b/ui/src/components/TriggerEditor.vue
new file mode 100644
index 000000000..d4026968c
--- /dev/null
+++ b/ui/src/components/TriggerEditor.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+ 左
+
+
+ |
+
+ (left = d)" />
+
+ {
+ left = v
+ }
+ "
+ />
+
+ |
+
+
+ 运算符 |
+
+
+ |
+
+
+
+
+ 右
+
+
+ |
+
+ (right = d)"
+ />
+
+ {
+ right = v
+ }
+ "
+ />
+
+ |
+
+
+
+
+
diff --git a/ui/src/components/TriggerString.vue b/ui/src/components/TriggerString.vue
new file mode 100644
index 000000000..b0b440bf5
--- /dev/null
+++ b/ui/src/components/TriggerString.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/buffer.vue b/ui/src/components/buffer.vue
new file mode 100644
index 000000000..829c3e0fd
--- /dev/null
+++ b/ui/src/components/buffer.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 第 {{ item.skill_key + 1 }} 个技能
+
+
+ {{ item.phase_level }}
+
+
+
+
+
+
+ {{ item.skillname }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/components/bufferinfo.vue b/ui/src/components/bufferinfo.vue
new file mode 100644
index 000000000..0bb5f52c1
--- /dev/null
+++ b/ui/src/components/bufferinfo.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/main.js b/ui/src/main.js
new file mode 100644
index 000000000..f2d423c09
--- /dev/null
+++ b/ui/src/main.js
@@ -0,0 +1,21 @@
+import { createApp, ref } from 'vue'
+import { createPinia } from 'pinia'
+import App from './App.vue'
+import router from './router.js'
+import axios from 'axios'
+import VueAxios from 'vue-axios'
+
+const app = createApp(App)
+app.use(router)
+app.use(VueAxios, axios)
+app.provide('axios', app.config.globalProperties.axios)
+app.use(createPinia())
+app.provide('loaded', ref(false))
+
+app.mount('#app')
+// main.js
+
+import { plugin as Slicksort } from 'vue-slicksort'
+
+// Enables groups and drag and drop functionality
+app.use(Slicksort)
diff --git a/ui/src/pages/BasementSkill copy.vue b/ui/src/pages/BasementSkill copy.vue
new file mode 100644
index 000000000..c78d54c30
--- /dev/null
+++ b/ui/src/pages/BasementSkill copy.vue
@@ -0,0 +1,208 @@
+
+
+ 名称搜索:
+
+ 驻站搜索:
+
+
+
+
+
+
+
diff --git a/ui/src/pages/BasementSkill.vue b/ui/src/pages/BasementSkill.vue
new file mode 100644
index 000000000..872345789
--- /dev/null
+++ b/ui/src/pages/BasementSkill.vue
@@ -0,0 +1,84 @@
+
+ 做了筛选名称,筛选描述,没做其他的效果显示,会有的.jpg
+
+ 名称搜索:
+
+ 描述搜索(如用拼音,全拼):
+
+
+
+
+
+
+
+ 干员名 |
+ 技能枚举 |
+ 等级 |
+ 技能名称 |
+ 进驻场所 |
+ 描述 |
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/Doc.vue b/ui/src/pages/Doc.vue
new file mode 100644
index 000000000..9fdf09090
--- /dev/null
+++ b/ui/src/pages/Doc.vue
@@ -0,0 +1,21 @@
+
+
+ 在线文档地址:
+
+ https://arkmowers.github.io/arknights-mower/
+
+
+
+
+
+
diff --git a/ui/src/pages/Log.vue b/ui/src/pages/Log.vue
new file mode 100644
index 000000000..ad61f2f7c
--- /dev/null
+++ b/ui/src/pages/Log.vue
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+ 时间 |
+ 任务 |
+
+
+
+
+
+
+
+ {{ task.time.split('T')[1].split('.')[0] }}
+ |
+ {{ key }} |
+
+ {{ value.map((x) => x || '_').join(', ') }}
+ |
+
+
+
+
+ {{ task.time.split('T')[1].split('.')[0] }}
+ |
+ {{ task.meta_data }}{{ task.type.display_value }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 立即停止
+
+
+
+
+
+
+
+
+
+ 开始执行
+
+
+
+
+
+
+
+
+
+ 新增任务
+
+
+ 目前只糊了一个勉强能用的版本,其他功能敬请期待
+ 只开放了空任务/专精任务
+ 只能增,不能删!!谨慎填写任务
+ 如果 mower 休息到 00:30,新增的 00:15 的任务是不会被执行的,因为此时在休息
+ 所以最好在 00:00 mower运行的时候添加 00:15 的任务了,考验手速的时候到了
+ 空任务,请确保任务房间名字,干员数量正确(没有判定)
+ 专精任务,UI有详细说明;新增完毕,UI上面的表会实时反馈
+ 在Q群或者频道提以上问题,看心情踢人
+
+
+
+
+ 自动滚动
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/Material_all_in_one.vue b/ui/src/pages/Material_all_in_one.vue
new file mode 100644
index 000000000..62992c6ef
--- /dev/null
+++ b/ui/src/pages/Material_all_in_one.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
diff --git a/ui/src/pages/NotFound.vue b/ui/src/pages/NotFound.vue
new file mode 100644
index 000000000..00cc211e2
--- /dev/null
+++ b/ui/src/pages/NotFound.vue
@@ -0,0 +1,3 @@
+
+ 404 Not Found
+
diff --git a/ui/src/pages/Plan.vue b/ui/src/pages/Plan.vue
new file mode 100644
index 000000000..7d0593853
--- /dev/null
+++ b/ui/src/pages/Plan.vue
@@ -0,0 +1,407 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新建副表
+
+
+
+
+
+ 编辑触发条件
+
+
+
+
+
+ 编辑任务
+
+
+
+
+
+ 删除此副表
+
+
+
+
+
+
+
+ 导入排班
+
+
+
+
+
+
+
+ 导出图片
+
+
+
+
+
+
+
+ 令夕模式
+
+ 令夕上班时起作用
+ 启动Mower前需要手动对齐心情
+ 感知:夕心情-令心情=12
+ 烟火:令心情-夕心情=12
+ 均衡:夕令心情一样
+
+
+
+
+ 感知信息
+ 人间烟火
+ 均衡模式
+
+
+
+
+ 最大组人数请查阅文档
+
+ 人
+
+
+
+ 需要回满心情的干员请查阅文档
+
+
+
+
+ 需要用尽心情的干员仅推荐写入具有暖机技能的干员
+
+
+
+
+
+ 0心情工作的干员心情涣散状态仍能触发技能的干员
+
+
+
+
+ 宿舍低优先级干员请查阅文档
+
+
+
+
+ 跑单时间刷新干员
+
+ 贸易站外影响贸易效率的干员
+
+ 默认情况下,mower 只在贸易站内干员换班后重读所有贸易站的订单剩余时间。
+ 若有贸易站外的干员影响贸易效率,且与贸易站内的干员不在一组,则需写入此选项中。
+
+
+
+
+
+
+
+ 宿舍黑名单
+ 不希望进行填充宿舍的干员
+
+
+
+
+
+
+
diff --git a/ui/src/pages/RecordLine.vue b/ui/src/pages/RecordLine.vue
new file mode 100644
index 000000000..da88e8750
--- /dev/null
+++ b/ui/src/pages/RecordLine.vue
@@ -0,0 +1,200 @@
+
+
+
干员心情折线表
+
+
+ {{ groupData.groupName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/RecordPie.vue b/ui/src/pages/RecordPie.vue
new file mode 100644
index 000000000..3da657f72
--- /dev/null
+++ b/ui/src/pages/RecordPie.vue
@@ -0,0 +1,93 @@
+
+
+
工作休息比例报表
+
+
+ {{ groupData.groupName }}
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/Settings.vue b/ui/src/pages/Settings.vue
new file mode 100644
index 000000000..f09b59906
--- /dev/null
+++ b/ui/src/pages/Settings.vue
@@ -0,0 +1,708 @@
+
+
+
+
+
+
+
+
+
+
+
+ 官服
+ BiliBili服
+
+
+
+
+
+ ...
+
+
+
+ ADB连接地址
+
+ 不同模拟器adb地址不同。如不填,系统会自动去寻找adb device中的第一个。
+ 夜神:127.0.0.1:62001
+
+
+
+
+
+
+
+ scrcpy-1.21-novideo
+ MaaTouch-1.1.0
+
+
+
+
+
+
+
+
+ 模拟器文件夹
+
+ 夜神:写到bin文件夹
+ MuMu12: 写到shell文件夹
+
+
+
+ ...
+
+
+
+ 多开编号
+
+ 除夜神单开选择-1以外,其他的按照改模拟器多开器中的序号。
+
+
+
+
+
+
+ 秒
+
+
+
+
+ 模拟器老板键
+
+ 启动模拟器后按此快捷键
+ 若不需要此功能,请留空
+ 加号分隔按键,不要空格
+
+ 按键名参考
+
+ KEYBOARD_KEYS
+
+
+
+
+
+
+
+
+
+
+ X:
+
+ Y:
+
+
+
+
+ 任务结束后退出游戏
+ 降低功耗
+
+
+
+
+ 任务结束后关闭模拟器
+ 减少空闲时的资源占用、避免模拟器长时间运行出现问题
+
+
+
+
+ 关闭MuMu模拟器12时结束adb进程
+
+ 运行命令taskkill /f /t /im adb.exe
+ 使用MuMu模拟器12时,若遇到adb断连问题,可尝试开启此选项
+
+
+
+
+ 启动后自动开始任务
+
+
+ 检查版本更新
+
+
+
+
+
+ ADB+Gzip无损压缩,兼容性好
+
+
+ DroidCast有损压缩,速度更快
+
+
+ 自定义命令向STDOUT
打印图像
+
+
+
+
+
+
+
+ 不旋转
+ 旋转180度
+
+
+
+
+
+
+ 测试
+
+
+
+
+
+ (截图用时{{ elapsed }}ms)
+
+
+
+
+ 毫秒
+
+
+
+
+ 截图保存时间
+ 可填小数
+
+
+ 小时
+
+
+
+
+
+
+ 场景 |
+ 截图间隔 |
+ 等待次数 |
+
+
+
+
+ {{ scene_name[key] }} |
+
+
+ 秒
+
+ |
+
+
+ 次
+
+ |
+
+
+
+
+
+
+
+
+ 应用
+
+
+
+
+ 使用托盘图标
+ 重启生效
+
+
+
+
+
+ 亮色
+ 暗色
+
+
+
+
+
+ 日常任务间隔
+
+ 可填小数
+ 清理智、日常/周常任务领取、借助战打OF-1
+
+
+
+ 小时
+
+
+
+
+
+
+
+
+
+
+ 宿舍黑名单
+
+ 不希望进行填充宿舍的干员
+
+
+
+
+
+
+
+ 跑单前置延时
+
+ 推荐范围5-10
+ 可填小数
+
+
+
+ 分钟
+
+
+
+ 葛朗台跑单
+
+
+
+ 葛朗台缓冲时间
+ 推荐范围:15-30
+
+
+ 秒
+
+
+
+
+ 跑单前返回主界面以保持登录状态
+
+
+
+
+ 无人机使用房间
+
+ 加速制造站为指定制造站加速
+ (加速任意贸易站)只会加速有跑单人员作备班的站
+ 例:没填龙舌兰但书的卖玉站 (加速任意贸易站) 不会被加速
+ 如需要加速特定某个贸易站请指定对应房间
+
+
+
+
+
+
+ 无人机使用阈值
+
+ 如加速贸易,推荐大于 贸易站数*x + 92
+ 如加速制造,推荐大于 贸易站数*x
+ 葛朗台跑单模式下x=0,非葛朗台推荐x=10
+
+
+
+
+
+
+ 无人机加速间隔
+
+ 可填小数
+
+
+
+ 小时
+
+
+
+
+
+
+
+ 心情阈值
+
+ 2电站推荐不低于65%
+ 3电站推荐不低于50%
+ 即将大更新推荐设置成80%
+
+
+
+
+
+ %
+
+
+
+
+
+ 宿舍不养闲人
+ 干员心情回满后,立即释放宿舍空位
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/basement_skill/buffer.json b/ui/src/pages/basement_skill/buffer.json
new file mode 100644
index 000000000..aec77afa5
--- /dev/null
+++ b/ui/src/pages/basement_skill/buffer.json
@@ -0,0 +1,713 @@
+{
+ "ba_sluggish": {
+ "termId": "ba.sluggish",
+ "termName": "停顿",
+ "description": "移动速度降低80%",
+ "buffer": []
+ },
+ "ba_root": {
+ "termId": "ba.root",
+ "termName": "束缚",
+ "description": "无法移动",
+ "buffer": []
+ },
+ "ba_stun": {
+ "termId": "ba.stun",
+ "termName": "晕眩",
+ "description": "无法移动、阻挡、攻击及使用技能",
+ "buffer": []
+ },
+ "ba_buffres": {
+ "termId": "ba.buffres",
+ "termName": "抵抗",
+ "description": "<$ba.stun>晕眩>、<$ba.cold>寒冷>、<$ba.frozen>冻结>等异常状态的持续时间减半(同名效果不叠加)",
+ "buffer": []
+ },
+ "ba_invisible": {
+ "termId": "ba.invisible",
+ "termName": "隐匿",
+ "description": "不阻挡时不成为敌方攻击的目标",
+ "buffer": []
+ },
+ "ba_camou": {
+ "termId": "ba.camou",
+ "termName": "迷彩",
+ "description": "不阻挡时不成为敌方普通攻击的目标(无法躲避溅射类攻击)",
+ "buffer": []
+ },
+ "ba_fragile": {
+ "termId": "ba.fragile",
+ "termName": "脆弱",
+ "description": "受到的物理、法术、真实伤害提升相应比例(同名效果取最高)",
+ "buffer": []
+ },
+ "ba_shield": {
+ "termId": "ba.shield",
+ "termName": "护盾",
+ "description": "每层护盾可以抵挡一次伤害",
+ "buffer": []
+ },
+ "ba_protect": {
+ "termId": "ba.protect",
+ "termName": "庇护",
+ "description": "受到的物理和法术伤害降低相应比例(同名效果取最高)",
+ "buffer": []
+ },
+ "ba_cold": {
+ "termId": "ba.cold",
+ "termName": "寒冷",
+ "description": "攻击速度下降30,如果在持续时间内再次受到寒冷效果则会变为<$ba.frozen>冻结>",
+ "buffer": []
+ },
+ "ba_frozen": {
+ "termId": "ba.frozen",
+ "termName": "冻结",
+ "description": "无法移动、攻击及使用技能(通过<$ba.cold>寒冷>触发);敌方被冻结时,法术抗性-15",
+ "buffer": []
+ },
+ "ba_sleep": {
+ "termId": "ba.sleep",
+ "termName": "沉睡",
+ "description": "无敌且无法行动",
+ "buffer": []
+ },
+ "ba_inspire": {
+ "termId": "ba.inspire",
+ "termName": "鼓舞",
+ "description": "获得额外附加的基础属性加成(同类属性取最高)",
+ "buffer": []
+ },
+ "ba_binding": {
+ "termId": "ba.binding",
+ "termName": "绑定",
+ "description": "绑定对象不在场时技能强制结束,清空所有技力且无法回复技力",
+ "buffer": []
+ },
+ "ba_dt_neural": {
+ "termId": "ba.dt.neural",
+ "termName": "神经损伤",
+ "description": "神经损伤累计至1000时,受到1000点真实伤害并<$ba.stun>晕眩>10秒",
+ "buffer": []
+ },
+ "ba_charged": {
+ "termId": "ba.charged",
+ "termName": "蓄力",
+ "description": "技力达到上限可继续回复,回复至上限2倍时进入蓄力状态,此时开启技能会触发额外效果(任何时候开启均消耗全部技力)",
+ "buffer": []
+ },
+ "ba_strong": {
+ "termId": "ba.strong",
+ "termName": "精力充沛",
+ "description": "生命值高于一定比例时攻击力加成提升相应比例(同名效果攻击力加成取最高)",
+ "buffer": []
+ },
+ "ba_dt_erosion": {
+ "termId": "ba.dt.erosion",
+ "termName": "侵蚀损伤",
+ "description": "侵蚀损伤累计至1000时,永久降低100点防御力并受到800点物理伤害",
+ "buffer": []
+ },
+ "ba_dt_burning": {
+ "termId": "ba.dt.burning",
+ "termName": "灼燃损伤",
+ "description": "灼燃损伤累计至1000时,10秒内降低20法术抗性并受到1200点法术伤害",
+ "buffer": []
+ },
+ "ba_float": {
+ "termId": "ba.float",
+ "termName": "近地悬浮",
+ "description": "无法被阻挡或近战攻击",
+ "buffer": []
+ },
+ "ba_refraction": {
+ "termId": "ba.refraction",
+ "termName": "折射",
+ "description": "生效时,法术抗性+70",
+ "buffer": []
+ },
+ "ba_dt_element": {
+ "termId": "ba.dt.element",
+ "termName": "元素损伤",
+ "description": "包括<$ba.dt.neural>神经损伤>、<$ba.dt.erosion>侵蚀损伤>、<$ba.dt.burning>灼燃损伤>、<$ba.dt.apoptosis>凋亡损伤>",
+ "buffer": []
+ },
+ "ba_overdrive": {
+ "termId": "ba.overdrive",
+ "termName": "过载",
+ "description": "技能持续拥有两段计量槽,技能进行到一半时触发额外效果",
+ "buffer": []
+ },
+ "ba_dt_apoptosis": {
+ "termId": "ba.dt.apoptosis",
+ "termName": "凋亡损伤",
+ "description": "凋亡损伤累计至1000时,15秒内无法开启技能,每秒损失1点技力并受到100点法术伤害",
+ "buffer": []
+ },
+ "ba_debuff": {
+ "termId": "ba.debuff",
+ "termName": "异常状态",
+ "description": "包括<$ba.stun>晕眩>、<$ba.cold>寒冷>、<$ba.frozen>冻结>等",
+ "buffer": []
+ },
+ "ba_levitate": {
+ "termId": "ba.levitate",
+ "termName": "浮空",
+ "description": "变为空中单位,无法移动、攻击及使用技能;对重量大于3的单位持续时间减半",
+ "buffer": []
+ },
+ "ba_physhield": {
+ "termId": "ba.physhield",
+ "termName": "物理护盾",
+ "description": "每层物理护盾可以抵挡一次物理伤害",
+ "buffer": []
+ },
+ "ba_magicfragile": {
+ "termId": "ba.magicfragile",
+ "termName": "法术脆弱",
+ "description": "受到的法术伤害提升相应比例(同名效果取最高)",
+ "buffer": []
+ },
+ "ba_elementfragile": {
+ "termId": "ba.elementfragile",
+ "termName": "元素脆弱",
+ "description": "受到的元素伤害提升相应比例(同名效果取最高)",
+ "buffer": []
+ },
+ "ba_dying": {
+ "termId": "ba.dying",
+ "termName": "重伤",
+ "description": "移速下降且无法被阻挡,10秒后自然死亡,被击杀后使击杀者回复数点技力",
+ "buffer": []
+ },
+ "ba_barrier": {
+ "termId": "ba.barrier",
+ "termName": "屏障",
+ "description": "可以吸收一定数值的伤害",
+ "buffer": []
+ },
+ "ba_weightless": {
+ "termId": "ba.weightless",
+ "termName": "失重",
+ "description": "重量下降一个等级(同名效果不可叠加)",
+ "buffer": []
+ },
+ "ba_berserk": {
+ "termId": "ba.berserk",
+ "termName": "坚忍",
+ "description": "根据已损失的生命值获得相应比例的属性加成,损失一定比例时达最大加成(同类属性取最高)",
+ "buffer": []
+ },
+ "ba_steal": {
+ "termId": "ba.steal",
+ "termName": "偷取",
+ "description": "减少目标的基础属性作为自身加成,目标减少和自身加成的属性不超过指定上限(同类属性取最高)",
+ "buffer": []
+ },
+ "ba_weaken": {
+ "termId": "ba.weaken",
+ "termName": "虚弱",
+ "description": "使目标攻击力降低相应比例(同名效果取最高)",
+ "buffer": []
+ },
+ "ba_dt_apoptosis2": {
+ "termId": "ba.dt.apoptosis2",
+ "termName": "凋亡损伤·我方",
+ "description": "累计满时爆发(普通、精英敌人1000点、领袖2000点累计值),使敌人附加50%<$ba.weaken>虚弱>但期间逐渐恢复,每秒受到800点元素伤害。15秒冷却",
+ "buffer": []
+ },
+ "ba_epbarrier": {
+ "termId": "ba.epbarrier",
+ "termName": "损伤屏障",
+ "description": "可以吸收一定数值的<$ba.dt.element>元素损伤>",
+ "buffer": []
+ },
+ "ba_tremble": {
+ "termId": "ba.tremble",
+ "termName": "战栗",
+ "description": "被阻挡后无法进行普通攻击",
+ "buffer": []
+ },
+ "ba_fear": {
+ "termId": "ba.fear",
+ "termName": "恐惧",
+ "description": "无法被阻挡并四散逃跑",
+ "buffer": []
+ },
+ "ba_dt_burning2": {
+ "termId": "ba.dt.burning2",
+ "termName": "灼燃损伤·我方",
+ "description": "累计满时爆发(普通、精英敌人1000点、领袖2000点累计值),敌人立即受到7000点元素伤害且期间法术抗性-20。10秒冷却",
+ "buffer": []
+ },
+ "ba_addash": {
+ "termId": "ba.addash",
+ "termName": "移动",
+ "description": "不退场,以当前血量在目标位置部署",
+ "buffer": []
+ },
+ "ba_magicpoint": {
+ "termId": "ba.magicpoint",
+ "termName": "魔力",
+ "description": "特殊技力,整场战斗继承",
+ "buffer": []
+ },
+ "ba_chant": {
+ "termId": "ba.chant",
+ "termName": "吟唱",
+ "description": "停止攻击一段时间后尝试开启技能,期间被打断则中止",
+ "buffer": []
+ },
+ "ba_laiosteam": {
+ "termId": "ba.laiosteam",
+ "termName": "莱欧斯小队",
+ "description": "包括玛露西尔、森西、莱欧斯、齐尔查克",
+ "buffer": []
+ },
+ "cc_bd_A_1": {
+ "termId": "cc.bd_A_1",
+ "termName": "念力",
+ "description": "拥有该基建技能的干员\n迷迭香",
+ "buffer": []
+ },
+ "cc_bd_A_2": {
+ "termId": "cc.bd_A_2",
+ "termName": "意识实体",
+ "description": "拥有该基建技能的干员\n迷迭香",
+ "buffer": []
+ },
+ "cc_bd_B_1": {
+ "termId": "cc.bd_B_1",
+ "termName": "徘徊旋律",
+ "description": "拥有该基建技能的干员\n黑键",
+ "buffer": []
+ },
+ "cc_bd_B_2": {
+ "termId": "cc.bd_B_2",
+ "termName": "怅惘和声",
+ "description": "拥有该基建技能的干员\n黑键",
+ "buffer": []
+ },
+ "cc_bd_B_3": {
+ "termId": "cc.bd_B_3",
+ "termName": "无词颂歌",
+ "description": "拥有该基建技能的干员\n塑心",
+ "buffer": []
+ },
+ "cc_bd_A": {
+ "termId": "cc.bd_A",
+ "termName": "思维链环",
+ "description": "可影响<$cc.bd_A_1><@cc.rem>念力>>、<$cc.bd_A_2><@cc.rem>意识实体>>相关技能\n由以下干员的基建技能提供\n迷迭香",
+ "buffer": [
+ "cc_bd_A_1",
+ "cc_bd_A_2"
+ ]
+ },
+ "cc_bd_B": {
+ "termId": "cc.bd_B",
+ "termName": "无声共鸣",
+ "description": "可影响<$cc.bd_B_1><@cc.rem>徘徊旋律>>、<$cc.bd_B_2><@cc.rem>怅惘和声>>、<$cc.bd_B_3><@cc.rem>无词颂歌>>相关技能\n由以下干员的基建技能提供\n黑键、塑心、深律",
+ "buffer": [
+ "cc_bd_B_1",
+ "cc_bd_B_2",
+ "cc_bd_B_3"
+ ]
+ },
+ "cc_bd_C": {
+ "termId": "cc.bd_C",
+ "termName": "巫术结晶",
+ "description": "拥有该基建技能的干员\n截云",
+ "buffer": []
+ },
+ "cc_bd_a1": {
+ "termId": "cc.bd_a1",
+ "termName": "感知信息",
+ "description": "可影响<$cc.bd_A><@cc.rem>思维链环>>、<$cc.bd_B><@cc.rem>无声共鸣>>相关变量\n由以下干员的基建技能提供\n迷迭香、黑键、夕、令、絮雨、爱丽丝、车尔尼",
+ "buffer": [
+ "cc_bd_A",
+ "cc_bd_B"
+ ]
+ },
+ "cc_bd_b1": {
+ "termId": "cc.bd_b1",
+ "termName": "人间烟火",
+ "description": "可影响<$cc.bd_C><@cc.rem>巫术结晶>>相关技能\n由以下干员的基建技能提供\n夕、令、重岳、乌有、桑葚",
+ "buffer": [
+ "cc_bd_C"
+ ]
+ },
+ "cc_bd_ash": {
+ "termId": "cc.bd_ash",
+ "termName": "情报储备",
+ "description": "由以下干员的基建技能提供\n灰烬",
+ "buffer": []
+ },
+ "cc_bd_tachanka": {
+ "termId": "cc.bd_tachanka",
+ "termName": "乌萨斯特饮",
+ "description": "由以下干员的基建技能提供\n战车",
+ "buffer": []
+ },
+ "cc_bd_malist": {
+ "termId": "cc.bd_malist",
+ "termName": "工程机器人",
+ "description": "由以下干员的基建技能提供\n至简",
+ "buffer": []
+ },
+ "cc_bd_a1_a1": {
+ "termId": "cc.bd_a1_a1",
+ "termName": "记忆碎片",
+ "description": "可影响<$cc.bd_a1><@cc.rem>感知信息>>相关变量\n由以下干员的基建技能提供\n絮雨",
+ "buffer": [
+ "cc_bd_a1"
+ ]
+ },
+ "cc_bd_a1_a2": {
+ "termId": "cc.bd_a1_a2",
+ "termName": "梦境",
+ "description": "可影响<$cc.bd_a1><@cc.rem>感知信息>>相关变量\n由以下干员的基建技能提供\n爱丽丝",
+ "buffer": [
+ "cc_bd_a1"
+ ]
+ },
+ "cc_bd_a1_a3": {
+ "termId": "cc.bd_a1_a3",
+ "termName": "小节",
+ "description": "可影响<$cc.bd_a1><@cc.rem>感知信息>>相关变量\n由以下干员的基建技能提供\n车尔尼",
+ "buffer": [
+ "cc_bd_a1"
+ ]
+ },
+ "cc_bd_costdrop": {
+ "termId": "cc.bd.costdrop",
+ "termName": "心情落差",
+ "description": "干员自身心情上限与当前心情值的差值",
+ "buffer": []
+ },
+ "cc_bd_felyne": {
+ "termId": "cc.bd_felyne",
+ "termName": "木天蓼",
+ "description": "可影响<$cc.bd_felyne_1><@cc.rem>可爱的艾露猫>>、<$cc.bd_felyne_2><@cc.rem>可靠的随从们>>相关技能\n由以下干员的基建技能提供\n火龙S黑角、麒麟R夜刀",
+ "buffer": [
+ "cc_bd_felyne_1",
+ "cc_bd_felyne_2"
+ ]
+ },
+ "cc_bd_felyne_1": {
+ "termId": "cc.bd_felyne_1",
+ "termName": "可爱的艾露猫",
+ "description": "拥有该基建技能的干员\n泰拉大陆调查团",
+ "buffer": []
+ },
+ "cc_bd_felyne_2": {
+ "termId": "cc.bd_felyne_2",
+ "termName": "可靠的随从们",
+ "description": "拥有该基建技能的干员\n泰拉大陆调查团",
+ "buffer": []
+ },
+ "cc_bd_dungeon": {
+ "termId": "cc.bd_dungeon",
+ "termName": "魔物料理",
+ "description": "由以下干员的基建技能提供\n森西",
+ "buffer": []
+ },
+ "cc_m_var1": {
+ "termId": "cc.m.var1",
+ "termName": "回收利用",
+ "description": "拥有该基建技能的干员\n红云",
+ "buffer": []
+ },
+ "cc_m_var2": {
+ "termId": "cc.m.var2",
+ "termName": "配合意识",
+ "description": "拥有该基建技能的干员\n槐琥",
+ "buffer": []
+ },
+ "cc_t_snsant1": {
+ "termId": "cc.t.snsant1",
+ "termName": "天道酬勤·α",
+ "description": "拥有该基建技能的干员\n雪雉",
+ "buffer": []
+ },
+ "cc_t_snsant2": {
+ "termId": "cc.t.snsant2",
+ "termName": "天道酬勤·β",
+ "description": "拥有该基建技能的干员\n雪雉",
+ "buffer": []
+ },
+ "cc_g_lgd": {
+ "termId": "cc.g.lgd",
+ "termName": "龙门近卫局",
+ "description": "包含以下干员\n陈、星熊、诗怀雅",
+ "buffer": []
+ },
+ "cc_g_lda": {
+ "termId": "cc.g.lda",
+ "termName": "鲤氏侦探事务所",
+ "description": "包含以下干员\n老鲤、阿、吽、槐琥",
+ "buffer": []
+ },
+ "cc_g_ussg": {
+ "termId": "cc.g.ussg",
+ "termName": "乌萨斯学生自治团",
+ "description": "包含以下干员\n早露、凛冬、真理、古米、烈夏",
+ "buffer": []
+ },
+ "cc_g_A1": {
+ "termId": "cc.g.A1",
+ "termName": "A1小队",
+ "description": "包含以下干员\n芙蓉、炎熔、米格鲁、芬、克洛丝",
+ "buffer": []
+ },
+ "cc_g_R6": {
+ "termId": "cc.g.R6",
+ "termName": "彩虹小队",
+ "description": "包含以下干员\n灰烬、战车、闪击、霜华、艾拉、医生、双月、导火索",
+ "buffer": []
+ },
+ "cc_g_Attack": {
+ "termId": "cc.g.Attack",
+ "termName": "进攻方",
+ "description": "包含以下干员\n灰烬、闪击、双月、导火索",
+ "buffer": []
+ },
+ "cc_g_Defence": {
+ "termId": "cc.g.Defence",
+ "termName": "防守方",
+ "description": "包含以下干员\n战车、霜华、艾拉、医生",
+ "buffer": []
+ },
+ "cc_g_sp": {
+ "termId": "cc.g.sp",
+ "termName": "异格",
+ "description": "包含所有异格干员",
+ "buffer": []
+ },
+ "cc_g_abyssal": {
+ "termId": "cc.g.abyssal",
+ "termName": "深海猎人",
+ "description": "包含以下干员\n歌蕾蒂娅、斯卡蒂、幽灵鲨、安哲拉、乌尔比安",
+ "buffer": []
+ },
+ "cc_g_psk": {
+ "termId": "cc.g.psk",
+ "termName": "红松骑士团",
+ "description": "包含以下干员\n焰尾、远牙、灰毫、野鬃、正义骑士号",
+ "buffer": []
+ },
+ "cc_g_karlan": {
+ "termId": "cc.g.karlan",
+ "termName": "喀兰贸易",
+ "description": "包含以下干员\n银灰、灵知、初雪、崖心、角峰、讯使、耶拉、极光、锏",
+ "buffer": []
+ },
+ "cc_g_sui": {
+ "termId": "cc.g.sui",
+ "termName": "岁",
+ "description": "包含以下干员\n年、夕、令、重岳、黍",
+ "buffer": []
+ },
+ "cc_g_glasgow": {
+ "termId": "cc.g.glasgow",
+ "termName": "格拉斯哥帮",
+ "description": "包含以下干员\n推进之王、摩根、达格达、因陀罗",
+ "buffer": []
+ },
+ "cc_g_rh": {
+ "termId": "cc.g.rh",
+ "termName": "莱茵生命",
+ "description": "包含以下干员\n赫默、伊芙利特、塞雷娅、白面鸮、梅尔、麦哲伦、多萝西、星源、缪尔赛思",
+ "buffer": []
+ },
+ "cc_g_sm": {
+ "termId": "cc.g.sm",
+ "termName": "萨米",
+ "description": "包含以下干员\n远山、柏喙、雪绒、提丰、寒檀、凛视",
+ "buffer": []
+ },
+ "cc_g_bs": {
+ "termId": "cc.g.bs",
+ "termName": "黑钢国际",
+ "description": "包含以下干员\n雷蛇、芙兰卡、杰西卡、香草、杏仁",
+ "buffer": []
+ },
+ "cc_c_abyssal2_1": {
+ "termId": "cc.c.abyssal2_1",
+ "termName": "特殊加成",
+ "description": "每有1个<$cc.g.abyssal><@cc.kw>深海猎人>>干员进驻制造站,则控制中枢给每个进驻<$cc.g.abyssal><@cc.kw>深海猎人>>的制造站提供<@cc.vup>5%>生产力,最多给单个制造站提供<@cc.vup>45%>生产力",
+ "buffer": [
+ "cc_g_abyssal",
+ "cc_g_abyssal"
+ ]
+ },
+ "cc_c_abyssal2_2": {
+ "termId": "cc.c.abyssal2_2",
+ "termName": "特殊加成",
+ "description": "每有1个<$cc.g.abyssal><@cc.kw>深海猎人>>干员进驻制造站,则控制中枢给每个进驻<$cc.g.abyssal><@cc.kw>深海猎人>>的制造站提供<@cc.vup>10%>生产力,最多给单个制造站提供<@cc.vup>90%>生产力",
+ "buffer": [
+ "cc_g_abyssal",
+ "cc_g_abyssal"
+ ]
+ },
+ "cc_c_abyssal2_3": {
+ "termId": "cc.c.abyssal2_3",
+ "termName": "特殊叠加规则",
+ "description": "无法与<$cc.m.var2><@cc.rem>配合意识>>进行叠加,且优先生效\n无法与<$cc.m.pow1><@cc.rem>自动化·α>>、<$cc.m.pow2><@cc.rem>自动化·β>>、<$cc.m.pow3><@cc.rem>仿生海龙>>进行叠加,且清零效果优先生效",
+ "buffer": [
+ "cc_m_var2",
+ "cc_m_pow1",
+ "cc_m_pow2",
+ "cc_m_pow3"
+ ]
+ },
+ "cc_c_room1": {
+ "termId": "cc.c.room1",
+ "termName": "部分设施",
+ "description": "包含以下设施\n发电站、人力办公室、会客室",
+ "buffer": []
+ },
+ "cc_c_room2": {
+ "termId": "cc.c.room2",
+ "termName": "其他设施",
+ "description": "包含以下设施\n发电站、制造站、贸易站、人力办公室、会客室",
+ "buffer": []
+ },
+ "cc_c_room3": {
+ "termId": "cc.c.room3",
+ "termName": "工作场所",
+ "description": "包含以下设施\n发电站、制造站、贸易站、人力办公室、会客室、控制中枢、训练室",
+ "buffer": []
+ },
+ "cc_c_skill": {
+ "termId": "cc.c.skill",
+ "termName": "部分技能",
+ "description": "包含以下技能\n左膀右臂、S.W.E.E.P.、零食网络、清理协议、替身、必要责任、护卫、小小的领袖、独善其身、笑靥如春、金盏花诗会、捍卫之道",
+ "buffer": []
+ },
+ "cc_t_strong2": {
+ "termId": "cc.t.strong2",
+ "termName": "特殊叠加规则",
+ "description": "无法单独与<$cc.t.snsant1><@cc.rem>天道酬勤·α>>、<$cc.t.snsant2><@cc.rem>天道酬勤·β>>进行叠加,且优先生效\n当<$cc.t.snsant1><@cc.rem>天道酬勤·α>>、<$cc.t.snsant2><@cc.rem>天道酬勤·β>>与其他技能进行叠加时,该技能会对此叠加效果进行叠加",
+ "buffer": [
+ "cc_t_snsant1",
+ "cc_t_snsant2",
+ "cc_t_snsant1",
+ "cc_t_snsant2"
+ ]
+ },
+ "cc_c_sui2_1": {
+ "termId": "cc.c.sui2_1",
+ "termName": "特殊比较规则",
+ "description": "在<@cc.kw>公事公办>,<@cc.kw>孤光共照>,<@cc.kw>巴别塔之帜>提供的<$cc.c.room2><@cc.kw>其他设施>>每小时心情恢复值中取最高生效",
+ "buffer": [
+ "cc_c_room2"
+ ]
+ },
+ "cc_m_pow1": {
+ "termId": "cc.m.pow1",
+ "termName": "自动化·α",
+ "description": "由以下干员提供\n温蒂、森蚺、异客",
+ "buffer": []
+ },
+ "cc_m_pow2": {
+ "termId": "cc.m.pow2",
+ "termName": "自动化·β",
+ "description": "由以下干员提供\n森蚺",
+ "buffer": []
+ },
+ "cc_m_pow3": {
+ "termId": "cc.m.pow3",
+ "termName": "仿生海龙",
+ "description": "由以下干员提供\n温蒂",
+ "buffer": []
+ },
+ "cc_t_flow_gold": {
+ "termId": "cc.t.flow_gold",
+ "termName": "赤金生产线",
+ "description": "每有<@cc.kw>1>间<@cc.kw>制造站>生产<@cc.kw>赤金>,则赤金生产线<@cc.kw>+1>",
+ "buffer": []
+ },
+ "cc_w_ncdeer1": {
+ "termId": "cc.w.ncdeer1",
+ "termName": "因果",
+ "description": "每当加工心情消耗<@cc.kw>4>以下的配方未产出副产品时,所消耗的每<@cc.kw>1>点心情转化为<@cc.kw>1>点<@cc.kw>因果>",
+ "buffer": []
+ },
+ "cc_w_ncdeer2": {
+ "termId": "cc.w.ncdeer2",
+ "termName": "业报",
+ "description": "每当加工心情消耗<@cc.kw>8>的配方未产出副产品时,所消耗的每<@cc.kw>1>点心情转化为<@cc.kw>1>点<@cc.kw>业报>",
+ "buffer": []
+ },
+ "cc_t_accmuguard1": {
+ "termId": "cc.t.accmuguard1",
+ "termName": "武道",
+ "description": "协助专精训练至1级时累计<@cc.kw>1>点、2级时累计<@cc.kw>2>点、3级时累计<@cc.kw>3>点,累计上限为<@cc.kw>3>点(协助者心情为0时,无法累计或消耗)",
+ "buffer": []
+ },
+ "cc_sk_manu1": {
+ "termId": "cc.sk.manu1",
+ "termName": "标准化类技能",
+ "description": "包含以下技能\n标准化·α、标准化·β",
+ "buffer": []
+ },
+ "cc_sk_manu2": {
+ "termId": "cc.sk.manu2",
+ "termName": "莱茵科技类技能",
+ "description": "包含以下技能\n莱茵科技·α、莱茵科技·β、莱茵科技·γ",
+ "buffer": []
+ },
+ "cc_sk_manu3": {
+ "termId": "cc.sk.manu3",
+ "termName": "红松骑士团类技能",
+ "description": "包含以下技能\n红松骑士团·α、红松骑士团·β",
+ "buffer": []
+ },
+ "cc_sk_manu4": {
+ "termId": "cc.sk.manu4",
+ "termName": "金属工艺类技能",
+ "description": "包含以下技能\n金属工艺·α、金属工艺·β",
+ "buffer": []
+ },
+ "cc_tag_op": {
+ "termId": "cc.tag.op",
+ "termName": "作业平台",
+ "description": "包含以下干员\nLancet-2、Castle-3、THRM-EX、正义骑士号、Friston-3、PhonoR-0",
+ "buffer": []
+ },
+ "cc_tag_knight": {
+ "termId": "cc.tag.knight",
+ "termName": "骑士",
+ "description": "包含以下干员\n耀骑士临光、临光、瑕光、鞭刃、焰尾、远牙、灰毫、野鬃、正义骑士号、砾、薇薇安娜",
+ "buffer": []
+ },
+ "cc_tag_durin": {
+ "termId": "cc.tag.durin",
+ "termName": "杜林族",
+ "description": "包含以下干员\n至简、桃金娘、褐果、杜林",
+ "buffer": []
+ },
+ "cc_tag_mh": {
+ "termId": "cc.tag.mh",
+ "termName": "怪物猎人小队",
+ "description": "包含以下干员\n火龙S黑角、麒麟R夜刀、泰拉大陆调查团",
+ "buffer": []
+ },
+ "cc_tag_dungeon": {
+ "termId": "cc.tag.dungeon",
+ "termName": "莱欧斯小队",
+ "description": "包含以下干员\n玛露西尔、莱欧斯、齐尔查克、森西",
+ "buffer": []
+ },
+ "cc_gvial": {
+ "termId": "cc.gvial",
+ "termName": "嘉维尔",
+ "description": "包含以下干员\n百炼嘉维尔、嘉维尔",
+ "buffer": []
+ },
+ "cc_tra_pepe": {
+ "termId": "cc.tra.pepe",
+ "termName": "特别独占订单",
+ "description": "由佩佩提供的特殊贵金属订单,该订单的获取时长固定,收益恒定,所需赤金交付数为0",
+ "buffer": []
+ }
+}
\ No newline at end of file
diff --git a/ui/src/pages/basement_skill/skill.json b/ui/src/pages/basement_skill/skill.json
new file mode 100644
index 000000000..48b5edd68
--- /dev/null
+++ b/ui/src/pages/basement_skill/skill.json
@@ -0,0 +1,12858 @@
+[
+ {
+ "key": 342,
+ "name": "莱欧斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "好奇心",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "饱餐的干劲",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_dungeon"
+ ],
+ "des": "进驻会客室时,每拥有<@cc.vup>1>点<$cc.bd_dungeon><@cc.rem>魔物料理>>,线索搜集速度额外提升<@cc.vup>2%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_bd_dungeon",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 341,
+ "name": "齐尔查克",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "半身人公会代表",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit7",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "熟悉的味道",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_dungeon"
+ ],
+ "des": "进驻贸易站时,每<@cc.vup>1>点<$cc.bd_dungeon><@cc.rem>魔物料理>><@cc.vup>+1%>订单效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd_bd_dungeon",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 340,
+ "name": "森西",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "资深料理人",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_dungeon"
+ ],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>,如果目标是<$cc.tag.dungeon><@cc.kw>莱欧斯小队>>干员,则恢复效果额外<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_senshi",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "森西大食堂",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_dungeon"
+ ],
+ "des": "进驻宿舍时,当前宿舍每级提供<@cc.vup>1>层<$cc.bd_dungeon><@cc.rem>魔物料理>>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_bd_dungeon",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 339,
+ "name": "玛露西尔",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "差遣使魔·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+20%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_marcille1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "差遣使魔·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_marcille2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "意想不到的美味",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_dungeon"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>1>点<$cc.bd_dungeon><@cc.rem>魔物料理>><@cc.vup>+1%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd_dungeon",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 338,
+ "name": "莎草",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "医疗专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:链愈师",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>链愈师>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_chainhealer",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 337,
+ "name": "衡沙",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "大巴扎管理学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit7",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 336,
+ "name": "娜仁图亚",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "无畏豪情",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+10>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "齐心沙盗",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,每间宿舍每级为<@cc.kw>贵金属>类配方的生产力<@cc.vup>+1%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&dorm1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 335,
+ "name": "佩佩",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "多面逢源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站每级<@cc.vup>+1>个订单上限",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit&trade1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "慧眼独到",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tra_pepe"
+ ],
+ "des": "进驻贸易站时,固定获取<$cc.tra.pepe><@cc.kw>特别独占订单>>(不视作违约订单),且该类订单<@cc.vdown>不受任何订单获取效率的影响>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_pepe",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 334,
+ "name": "渡桥",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "懂行",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "天生的顾问",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+15%>,会客室每级额外提供<@cc.vup>5%>获取效率,最多提供<@cc.vup>30%>效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&meet",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 333,
+ "name": "锡人",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "梅兰德侦探·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+5%>,每间宿舍每级额外<@cc.vup>+1%>人脉资源的联络速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&dorm1",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "梅兰德侦探·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+5%>,每间宿舍每级额外<@cc.vup>+2%>人脉资源的联络速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&dorm2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 332,
+ "name": "妮芙",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "静心仪式·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "静心仪式·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "巡心",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有宿舍内所有干员的心情每小时恢复<@cc.vup>+0.05>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_leader",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 331,
+ "name": "乌尔比安",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“问题应当准确”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>与<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard&supporter",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“手段应当有效”",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_abyssal"
+ ],
+ "des": "进驻训练室协助位时,心情每小时消耗<@cc.vdown>+1>,基建内(不包含副手)每名<$cc.g.abyssal><@cc.kw>深海猎人>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>5>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_abyssal",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 330,
+ "name": "深巡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "对陆接洽代表·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>;当<@cc.kw>乌尔比安>在基建内时(不包含副手),订单获取效率额外<@cc.vup>+5%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_ord_spd_ext0",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "对陆接洽代表·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>;当<@cc.kw>乌尔比安>在基建内时(不包含副手),订单获取效率额外<@cc.vup>+10%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_ord_spd_ext1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 329,
+ "name": "海霓",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "穹顶物流管理·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "穹顶物流管理·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd3",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 328,
+ "name": "PhonoR-0",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "挽歌充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "咒文共鸣",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,如果<@cc.kw>逻各斯>进驻在训练室协助位,则无人机充能速度<@cc.vup>+5%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd_P1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 327,
+ "name": "历阵锐枪芬",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "重聚时光",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_A1"
+ ],
+ "des": "进驻制造站时,当前制造站内每个<$cc.g.A1><@cc.kw>A1小队>>干员为自身<@cc.vup>+10%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_A1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 326,
+ "name": "魔王",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "魔王传承",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>阿米娅>一起进驻控制中枢时,自身和<@cc.kw>阿米娅>心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_amiya",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "“未完的故事”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>阿米娅>一起进驻控制中枢时,自身和<@cc.kw>阿米娅>心情每小时恢复<@cc.vup>+0.1>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_amiya1",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "期冀之汇",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,会客室线索搜集速度<@cc.vup>+15%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_spd1",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 325,
+ "name": "逻各斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "女妖之力",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>与<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster&supporter1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "言语之义",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室单次协助一名干员训练时长达<@cc.vup>5>小时,则该名干员下次训练所需时间<@cc.vup>-50%>(任意一方离开训练室时,效果消失)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_reduceTime",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 324,
+ "name": "维什戴尔",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "同谋·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,当<@cc.kw>伊内丝>入驻会客室时,会客室线索搜集速度<@cc.vup>+5%>;当<@cc.kw>赫德雷>入驻贸易站时,赫德雷所在贸易站订单上限<@cc.vup>+1>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_babel",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "同谋·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,当<@cc.kw>伊内丝>入驻会客室时,会客室线索搜集速度<@cc.vup>+5%>;当<@cc.kw>赫德雷>入驻贸易站时,赫德雷所在贸易站订单上限<@cc.vup>+2>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_babel2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "巴别塔之帜",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_room2",
+ "cc_c_sui2_1"
+ ],
+ "des": "进驻控制中枢时,<$cc.c.room2><@cc.kw>其他设施>>内处于工作状态的干员心情每小时恢复<@cc.vup>+0.1>,当<@cc.kw>魔王>进驻中枢时额外<@cc.vup>+0.1>(与控制中枢加成有<$cc.c.sui2_1><@cc.rem>特殊比较规则>>)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_expand",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 323,
+ "name": "露托",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "一丝不苟",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "高效回收",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 322,
+ "name": "奥达",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“职业操守”·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>15%>,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost_condChar2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "“职业操守”·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>35%>,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost_condChar3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "暗线",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,连续消耗超过<@cc.kw>16>点心情下一次<@cc.kw>必定>获得<@cc.kw>罗德岛制药>的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&condChar_mustget2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 321,
+ "name": "阿罗玛",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "净味香氛",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+25%>,心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold4",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "例行清扫",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站后,生产力每小时<@cc.vup>+2%>,最终达到<@cc.vup>+20%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 320,
+ "name": "阿斯卡纶",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "S.W.E.E.P.主管",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,如训练室有干员在进行技能专精,则该干员的专精技能训练速度<@cc.vup>+5%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_train_spd1",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "情报主脑",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有贸易站订单效率<@cc.vup>+7%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_t_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 319,
+ "name": "导火索",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "战术指导·进攻",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_Attack"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.g.Attack><@cc.kw>进攻方>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>4>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_attack",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "实干的寡言者",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_tachanka"
+ ],
+ "des": "进驻制造站时,生产力<@cc.vup>+20%>,每有<@cc.vup>1>瓶<$cc.bd_tachanka><@cc.rem>乌萨斯特饮>>,则仓库容量上限<@cc.vup>+2>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_fuze",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 318,
+ "name": "医生",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "战术指导·防守",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_Defence"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.g.Defence><@cc.kw>防守方>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>4>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defense",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "利他主义",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 317,
+ "name": "双月",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "战术指导·进攻",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_Attack"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.g.Attack><@cc.kw>进攻方>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>4>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_attack",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "情报专家",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_ash"
+ ],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>5%>,同时每<@cc.vup>1>点<$cc.bd_ash><@cc.rem>情报储备>>额外提升<@cc.vup>5%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_iana",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 316,
+ "name": "艾拉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "战术指导·防守",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_Defence"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.g.Defence><@cc.kw>防守方>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>4>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defense",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "反抗者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,更容易获得线索板上尚未拥有的线索,但控制中枢内除自身以外的其余干员心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_ela",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 315,
+ "name": "红隼",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "战纹",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有宿舍内所有干员的心情每小时恢复<@cc.vup>+0.05>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_leader",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "捍卫之道",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 314,
+ "name": "万顷",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天师府工艺",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>65%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p4",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "勤学不倦",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_all_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 313,
+ "name": "小满",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "乡野笛音",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 312,
+ "name": "左乐",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "学无不精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,干员的专精技能训练速度<@cc.vup>+25%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_all",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "思而后行",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_accmuguard1"
+ ],
+ "des": "进驻训练室协助位时,每完整协助完成1次专精训练,累计一定<$cc.t.accmuguard1><@cc.rem>武道>>。<$cc.t.accmuguard1><@cc.rem>武道>>达到上限时,下一次开始协助<@cc.kw>近卫>干员训练专精技能至<@cc.kw>1>级将立即完成,并消耗已有<$cc.t.accmuguard1><@cc.rem>武道>>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_accmu_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 311,
+ "name": "黍",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "春雷响,万物长",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内所有干员心情每小时消耗<@cc.vup>-0.1>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_cost_room1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "稻禾厚,顺秋收",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>3>点<$cc.bd_b1><@cc.rem>人间烟火>><@cc.vup>+1%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd7",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 310,
+ "name": "温米",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "大锅饭·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#22cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "大锅饭·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "金属工艺·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 309,
+ "name": "莱伊",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "探井人·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "探井人·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "岩体锚固",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>产出<@cc.kw>T3>品质的副产品时,副产品必定为<@cc.kw>固源岩组>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_drop_orirock",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 308,
+ "name": "哈洛德",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "交个朋友",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,<@cc.kw>提升>会客室内另一干员<@cc.kw>所属派系的线索倾向效果>(如会客室没有另一干员则不提升)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskil_meet_team&char",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 307,
+ "name": "烈夏",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "患难拍档",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,若<@cc.kw>古米>在贸易站,则<@cc.kw>作战记录>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_formula_spd_sunbr",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 306,
+ "name": "锏",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "威压",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>,且订单上限<@cc.vdown>-2>(订单最少为1)",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit_down1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "不怒自威",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>,且订单上限<@cc.vdown>-6>(订单最少为1)",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit_down2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "冠军风采",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站内干员提升的每<@cc.vup>5>个订单上限,提供<@cc.vup>25%>订单获取效率,最多提供<@cc.vup>100%>效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit2spd",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 305,
+ "name": "跃跃",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "无辜笑脸",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,处于线索交流时线索搜集速度提升<@cc.vup>30%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_exchange",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 304,
+ "name": "深律",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "宫廷礼仪",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+10%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost5",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "威权谕使",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost6",
+ "buffColor": "#565657",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "心声图绘",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B"
+ ],
+ "des": "进驻人力办公室时,每个招募位(不包含初始招募位)<@cc.vup>+15>点<$cc.bd_B><@cc.rem>无声共鸣>>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_bd_B",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 303,
+ "name": "折光",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "鉴定师的眼光",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "鉴定师的手段",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 302,
+ "name": "止颂",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "苦修之律",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,相应配方的心情消耗恒定为<@cc.vup>4>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_constant2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "虔修之律",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,相应配方的心情消耗恒定为<@cc.vup>3>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_constant3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "巫妖学识",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 301,
+ "name": "薇薇安娜",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "金盏花诗会",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "烛骑士微光",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_knight"
+ ],
+ "des": "进驻控制中枢时,每个进驻在制造站的<$cc.tag.knight><@cc.kw>骑士>>干员生产力<@cc.vup>+7%>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_fraction_knight",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 300,
+ "name": "塑心",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "无声共鸣",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B"
+ ],
+ "des": "进驻宿舍时,该宿舍内每有<@cc.vup>1>名干员,<$cc.bd_B><@cc.rem>无声共鸣>><@cc.vup>+1>",
+ "roomType": "宿舍",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_dorm_bdnum",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "无词颂歌",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B"
+ ],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>,每有<@cc.vup>5>点<$cc.bd_B><@cc.rem>无声共鸣>>,该宿舍内所有干员的心情每小时恢复额外<@cc.vup>+0.01>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_rec_allbd",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 299,
+ "name": "维荻",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "谨慎加工",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "DIY·酮凝集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>酮凝集>类材料时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_Ketone",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 298,
+ "name": "刺玫",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "芬芳疗养·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all4",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "芬芳疗养·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员心情每小时恢复<@cc.vup>+0.15>,同时该宿舍内心情<@cc.vup>18>以下的干员恢复效果额外<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all_tired",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 297,
+ "name": "戴菲恩",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "旧识新交",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_glasgow"
+ ],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,每产生<@cc.vup>1>次联络次数,增加<$cc.g.glasgow><@cc.kw>格拉斯哥帮>>线索的概率(工作时长和招募位影响概率)",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&glasgow",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "运筹好手",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_glasgow"
+ ],
+ "des": "进驻控制中枢时,同一贸易站中,每有<@cc.vup>1>名<$cc.g.glasgow><@cc.kw>格拉斯哥帮>>干员,订单获取效率<@cc.vup>+10%>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_g_limit&spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 296,
+ "name": "赫德雷",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "白手起家·α",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_room3"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>;当<@cc.kw>伊内丝>入驻<$cc.c.room3><@cc.kw>工作场所>>时(不包含副手),订单获取效率额外<@cc.vup>+5%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_par&per1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "白手起家·β",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_room3"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>;当<@cc.kw>伊内丝>、<@cc.kw>W>入驻<$cc.c.room3><@cc.kw>工作场所>>时(不包含副手),订单获取效率分别额外<@cc.vup>+5%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_par&per2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 295,
+ "name": "冰酿",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "笑靥如春",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "小酌怡情",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使心情未满的宿舍成员,平均分配到总计每小时心情恢复<@cc.vup>+0.8>的加成(该技能对单名干员和多名干员生效时,同种效果分别取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&single",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 294,
+ "name": "杏仁",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "小奇思",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+25%>,心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "挑大梁",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_bs"
+ ],
+ "des": "进驻制造站时,基建内(不包含副手)每有1名<$cc.g.bs><@cc.kw>黑钢国际>>干员(最多<@cc.kw>3>名),<@cc.kw>贵金属>类配方的生产力<@cc.vup>+2%>,心情每小时消耗<@cc.vup>-0.15>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold&blacksteel",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 293,
+ "name": "涤火杰西卡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单管理·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,且订单上限<@cc.vup>+4>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "老友相聚",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_bs"
+ ],
+ "des": "进驻控制中枢时,自身心情每小时消耗<@cc.vdown>+0.5>;每个进驻在制造站的<$cc.g.bs><@cc.kw>黑钢国际>>干员,生产力<@cc.vup>+5%>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_bd_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 292,
+ "name": "纯烬艾雅法拉",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "医疗专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:行医",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>行医>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_wandermedic",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "火山温泉浴",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,基建内(不包括副手)每名<@cc.kw>行医>干员为该宿舍内所有干员心情每小时恢复速度<@cc.vup>+0.06>(最多生效<@cc.kw>4>名,且同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_rec_all&profession",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 291,
+ "name": "青枳",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "突发好运",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "不倒霉的一天",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-2>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build_cost2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 290,
+ "name": "琳琅诗怀雅",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "招商引资",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站内干员提升的每个订单上限,提供<@cc.vup>4%>订单获取效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_trade_ord_spd_variable",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 289,
+ "name": "苍苔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "金属工艺·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "打工心得",
+ "buffer": true,
+ "buffer_des": [
+ "cc_sk_manu4"
+ ],
+ "des": "进驻制造站时,当前制造站内每个<$cc.sk.manu4><@cc.kw>金属工艺类技能>>为自身<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_skill_spd3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 288,
+ "name": "凛视",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "远见",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,当与<@cc.kw>提丰>进驻会客室一起工作时,线索搜集速度额外提升<@cc.vup>15%>,自身心情每小时消耗<@cc.vdown>+0.5>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&bd",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "未来之途",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,当与<@cc.kw>提丰>进驻会客室一起工作时,线索搜集速度额外提升<@cc.vup>15%>,自身心情每小时消耗<@cc.vdown>+0.5>,且更容易获得线索板上尚未拥有的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&bd¬Owned",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 287,
+ "name": "寒檀",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "降生于冰寒",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sm"
+ ],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高),如果目标是<$cc.g.sm><@cc.kw>萨米>>干员,则恢复效果额外<@cc.vup>+0.45>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single_sami",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "雪祀候补",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sm"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.g.sm><@cc.kw>萨米>>干员为当前干员的专精技能训练速度<@cc.vup>+10%>(最多生效<@cc.kw>3>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sami",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 286,
+ "name": "提丰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "冰原游弋",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sm"
+ ],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>+10%>,与其他<$cc.g.sm><@cc.kw>萨米>>干员进驻会客室一起工作时,线索搜集速度额外提升<@cc.vup>+5%>,自身心情每小时消耗<@cc.vdown>+0.5>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&sami",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:攻城手",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>攻城手>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_siegesniper",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 285,
+ "name": "隐现",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "交游广阔",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>,同时每个招募位(不包含初始招募位)额外<@cc.vup>+5%>会客室线索搜集速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&clue2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "甜甜圈派对",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.4>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.4>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one12",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 284,
+ "name": "空构",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "技术交流·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度首小时<@cc.vup>+10%>,此后每小时<@cc.vup>+1%>,最终达到<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_power_rec_spd&addition1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "技术交流·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度首小时<@cc.vup>+15%>,此后每小时<@cc.vup>+1%>,最终达到<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_power_rec_spd&addition2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "外卖水果挞",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,心情每小时消耗<@cc.vup>-0.3>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd&cost",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 283,
+ "name": "圣约送葬人",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "公证所教习·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+20%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp4",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "公证所教习·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "合理利用",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产<@cc.kw>作战记录>类配方时,仓库容量上限<@cc.vup>+4>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp&limit3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 282,
+ "name": "Friston-3",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "备用能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "“愉快的对谈”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,如果<@cc.kw>凯尔希>进驻在控制中枢,则无人机充能速度<@cc.vup>+5%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd_P",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 281,
+ "name": "玫拉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "最终调试",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 280,
+ "name": "淬羽赫默",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "不停歇的电话",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "莱茵科技·γ",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd3",
+ "buffColor": "#ffd801",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 279,
+ "name": "霍尔海雅",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“职业操守”·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>15%>,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost_condChar2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "“职业操守”·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>35%>,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost_condChar3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "文献学顾问",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,连续消耗超过<@cc.kw>16>点心情下一次<@cc.kw>必定>获得<@cc.kw>莱茵生命>的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&condChar_mustget",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 278,
+ "name": "缪尔赛思",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天生丽质",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.55>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "生态科主任",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_rh"
+ ],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>,基建内(不包含副手)每有1名除自身以外的<$cc.g.rh><@cc.kw>莱茵生命>>干员(最多<@cc.kw>5>名),充能速度额外<@cc.vup>+3%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_power_rec_rhine",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 277,
+ "name": "休谟斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "修修还能用",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "省省能更多",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工原始心情消耗为<@cc.kw>2>的<@cc.kw>精英材料>时,副产品的产出概率额外提升<@cc.vup>40%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_rub",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 276,
+ "name": "摩根",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "头号陪练",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_glasgow"
+ ],
+ "des": "进驻宿舍时,<@cc.kw>推进之王>对该宿舍中<$cc.g.glasgow><@cc.kw>格拉斯哥帮>>干员恢复效果额外<@cc.vup>+0.3>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_toone1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "帮派指南针",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_glasgow"
+ ],
+ "des": "进驻贸易站时,同个贸易站中每有1名<$cc.g.glasgow><@cc.kw>格拉斯哥帮>>干员,当前贸易站订单获取效率<@cc.vup>+20%>;当与<@cc.kw>推进之王>在同一个贸易站时,订单获取效率额外<@cc.vup>+35%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_par1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 275,
+ "name": "洋灰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "掘进工程",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+10>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost5",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 274,
+ "name": "伊内丝",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "人事管理·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+35%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd3",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "聚影",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>,此后每小时提升<@cc.vup>2%>,最终达到<@cc.vup>30%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd_hast1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 273,
+ "name": "U-Official",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天真的谈判者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,当前贸易站<@cc.kw>赤金订单>的<@cc.kw>赤金>交付数必定为<@cc.kw>2>(不视作违约订单)",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&wt1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "显眼的调查者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得线索板上已经拥有的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spdOwned1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 272,
+ "name": "泰拉大陆调查团",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "可爱的艾露猫",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_felyne"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+5%>,且订单上限<@cc.vup>+2>,同时每有1个<$cc.bd_felyne><@cc.rem>木天蓼>>,则订单获取效率<@cc.vup>+3%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit_felyne",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "可靠的随从们",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_felyne"
+ ],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,生产力<@cc.vup>+5%>,同时每有1个<$cc.bd_felyne><@cc.rem>木天蓼>>,则生产力<@cc.vup>+1%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit_felyne",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 271,
+ "name": "火龙S黑角",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "团队合作",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_felyne",
+ "cc_tag_mh"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每有1名<$cc.tag.mh><@cc.kw>怪物猎人小队>>干员,则<$cc.bd_felyne><@cc.rem>木天蓼>><@cc.vup>+2>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_felyne",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "秘传交涉术",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_mh"
+ ],
+ "des": "当与<$cc.tag.mh><@cc.kw>怪物猎人小队>>干员进驻控制中枢一起工作时,所有贸易站订单效率<@cc.vup>+7%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_token_t_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 270,
+ "name": "麒麟R夜刀",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "耐力回复",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_felyne"
+ ],
+ "des": "进驻控制中枢时,自身心情每小时消耗<@cc.vdown>+0.5>,<$cc.bd_felyne><@cc.rem>木天蓼>><@cc.vup>+8>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_felyne",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "以身作则",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_mh"
+ ],
+ "des": "当与<$cc.tag.mh><@cc.kw>怪物猎人小队>>干员进驻控制中枢一起工作时,所有制造站生产力<@cc.vup>+2%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_token_p_spd2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 269,
+ "name": "铎铃",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "跋山涉水",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻贸易站时,贸易站内全体干员每小时心情消耗<@cc.vup>-0.1>,每有<@cc.vup>10>点<$cc.bd_b1><@cc.rem>人间烟火>>,则额外<@cc.vup>-0.01>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_bd_cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "万里传书",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻贸易站时,贸易站内全体干员每小时心情消耗<@cc.vup>-0.1>,每有<@cc.vup>10>点<$cc.bd_b1><@cc.rem>人间烟火>>,则额外<@cc.vup>-0.02>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_bd_cost2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 268,
+ "name": "仇白",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:领主",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>领主>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_lord",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 267,
+ "name": "截云",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "古老巫术",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_C",
+ "cc_bd_b1"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>5>点<$cc.bd_b1><@cc.rem>人间烟火>>转化为<@cc.vup>1>点<$cc.bd_C><@cc.rem>巫术结晶>>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_bd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "逐水草",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_C"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>1>点<$cc.bd_C><@cc.rem>巫术结晶>><@cc.vup>+1%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd5",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "问枯荣",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_C"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>1>点<$cc.bd_C><@cc.rem>巫术结晶>><@cc.vup>+2%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd6",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 266,
+ "name": "火哨",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "暖场",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,贸易站内干员心情每小时消耗<@cc.vup>-0.1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "代为说项",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,贸易站内除自身以外每名处于工作状态的干员<@cc.vup>+15%>订单获取效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_share1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 265,
+ "name": "林",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特殊渠道",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "用人唯才",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,每个招募位(不包含初始招募位)<@cc.vup>+10%>人脉资源的联络速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&extra1",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 264,
+ "name": "重岳",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "知我为我",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1",
+ "cc_g_sui"
+ ],
+ "des": "进驻控制中枢时,自身心情每小时消耗<@cc.vdown>+0.5>;每个<$cc.g.sui><@cc.kw>岁>>干员进驻在宿舍以外的设施则<$cc.bd_b1><@cc.rem>人间烟火>><@cc.vup>+5>(最多5名)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_bd3",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "孤光共照",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1",
+ "cc_c_room2",
+ "cc_c_sui2_1"
+ ],
+ "des": "进驻控制中枢时,<$cc.c.room2><@cc.kw>其他设施>>内处于工作状态的干员心情每小时恢复<@cc.vup>+0.05>;同时每有<@cc.vup>20>点<$cc.bd_b1><@cc.rem>人间烟火>>,则额外<@cc.vup>+0.05>,(与控制中枢加成有<$cc.c.sui2_1><@cc.rem>特殊比较规则>>)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_bd4",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 263,
+ "name": "焰影苇草",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "红龙之血",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>与<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster&medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "红龙之血",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>与<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster&medic2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "领袖",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有宿舍内所有干员的心情每小时恢复<@cc.vup>+0.05>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_leader",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 262,
+ "name": "和弦",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "双面间谍",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>50%>,心情每小时消耗<@cc.vdown>+2>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost_condChar",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 261,
+ "name": "谜图",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "情报分析",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得线索板上尚未拥有的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spdNotOwned1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 260,
+ "name": "石英",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "精准排期",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,制造站每有<@cc.kw>1>类配方进行加工,则当前贸易站订单获取效率额外<@cc.vup>+2%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&formula1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 259,
+ "name": "雪绒",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "救援队·外界联络",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "救援队·保证体力",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+35%>,同时每个招募位(不包含初始招募位)使心情每小时消耗<@cc.vup>-0.1>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost3",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 258,
+ "name": "伺夜",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "领袖外交",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>25%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "新城贸易",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>,会客室每级额外提供<@cc.vup>5%>获取效率,最多提供<@cc.vup>40%>效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&meet1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 257,
+ "name": "子月",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "幼狼情性",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "荒野生存",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 256,
+ "name": "斥罪",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "法为正典",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+50%>,心情每小时消耗<@cc.vdown>+0.5>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_skgoat2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "寻同路人",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员心情每小时恢复<@cc.vup>+0.15>,同时每个招募位(不包含初始招募位)额外<@cc.vup>+0.05>恢复效果(叠加后的最终值同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_hireToRecAll1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 255,
+ "name": "缄默德克萨斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "沉默为剑",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>与<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist&pioneer1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "破斩桎梏",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>与<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_specialist&pioneer1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 254,
+ "name": "铅踝",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "模糊视线",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_costdrop"
+ ],
+ "des": "进驻制造站时,生产力<@cc.vup>+30%>,自身每有<@cc.kw>4>点<$cc.bd.costdrop><@cc.kw>心情落差>>,生产力<@cc.vdown>-5%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_reduce",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "窗外雪啸",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_costdrop"
+ ],
+ "des": "进驻制造站时,当自身<$cc.bd.costdrop><@cc.kw>心情落差>>大于<@cc.kw>12>时,生产力<@cc.vup>+10%>,仓库容量<@cc.vup>+6>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add&cost",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 253,
+ "name": "达格达",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:斗士",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>斗士>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_fighter",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 252,
+ "name": "明椒",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "朝气蓬勃",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有贸易站订单效率<@cc.vup>+7%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_t_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "裁缝·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "裁缝·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 251,
+ "name": "白铁",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "心相连",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,宿舍内每有<@cc.kw>10>名心情<@cc.vup>12>以下的干员时,心情消耗为<@cc.kw>4>的配方心情消耗全部<@cc.vup>-1>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_cost&dorm",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "意相通",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,宿舍内每有<@cc.kw>1>名心情<@cc.vup>12>以下的干员,副产品的产出概率提升<@cc.vup>5%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_dorm2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 250,
+ "name": "罗小黑",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "御灵之力·金",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工原始心情消耗为<@cc.kw>8>的<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_lolxh",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "化猫",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,心情消耗为<@cc.kw>4>以上的配方全部除以<@cc.vup>4>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_cost_lolxh",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 249,
+ "name": "海沫",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "意识兼容",
+ "buffer": true,
+ "buffer_des": [
+ "cc_sk_manu2",
+ "cc_sk_manu3"
+ ],
+ "des": "进驻制造站时,当前制造站内所有<$cc.sk.manu2><@cc.kw>莱茵科技类>>、<$cc.sk.manu3><@cc.kw>红松骑士团类>>技能也全都视作<@cc.kw>标准化类>技能",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_skill_change",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 248,
+ "name": "但书",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "合同法",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,如果下笔<@cc.kw>赤金订单>交付数小于<@cc.kw>4>,则视为<@cc.kw>违约订单>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_law",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "违约索赔·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,如果下笔<@cc.kw>赤金订单>是<@cc.kw>违约订单>,则<@cc.kw>赤金>交付数额外<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_against",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "违约索赔·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,如果下笔<@cc.kw>赤金订单>是<@cc.kw>违约订单>,则<@cc.kw>赤金>交付数额外<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_against2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 247,
+ "name": "玛恩纳",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独善其身",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "公事公办",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_room1",
+ "cc_c_room2",
+ "cc_c_skill"
+ ],
+ "des": "进驻控制中枢时,<$cc.c.room1><@cc.kw>部分设施>>内处于工作状态的干员心情每小时恢复<@cc.vup>+0.1>;同时控制中枢内<$cc.c.skill><@cc.kw>部分技能>>可以为<$cc.c.room2><@cc.kw>其他设施>>内处于工作状态的干员额外提供心情恢复",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_lonely",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 246,
+ "name": "至简",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "绘图设计",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_malist"
+ ],
+ "des": "进驻制造站时,基建内每间设施每级<@cc.vup>+1>个<@cc.kw><$cc.bd_malist>工程机器人>>,上限<@cc.vup>64>个",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_constrLv",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "机械辅助·α",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_malist"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>16>个<@cc.kw><$cc.bd_malist>工程机器人>><@cc.vup>+5%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "机械辅助·β",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_malist"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>8>个<@cc.kw><$cc.bd_malist>工程机器人>><@cc.vup>+5%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd4",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 245,
+ "name": "晓歌",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "不容遗漏·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得线索板上尚未拥有的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spdNotOwned1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "不容遗漏·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得线索板上尚未拥有的线索",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spdNotOwned2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 244,
+ "name": "鸿雪",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "销路宣发",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold"
+ ],
+ "des": "进驻贸易站时,每有<@cc.kw>1>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>,则当前贸易站订单获取效率<@cc.vup>+5%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_gs",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "际崖居民",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold",
+ "cc_tag_durin"
+ ],
+ "des": "进驻贸易站时,基建内每<@cc.kw>1>名<$cc.tag.durin><@cc.kw>杜林族>>干员(最多<@cc.kw>4>名)为当前贸易站提供<@cc.kw>1>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_durin",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 243,
+ "name": "百炼嘉维尔",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战指导",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+65%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_guard2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "过量训练",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助<@cc.kw>近卫>干员训练专精技能至<@cc.vup>3>级时,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_gavial",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 242,
+ "name": "星源",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "莱茵科技·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "莱茵科技·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 241,
+ "name": "承曦格雷伊",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "巡线框架",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,每<@cc.vup>10>架无人机上限<@cc.vup>+1%>无人机充能速度",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_drone",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "晨曦",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_op"
+ ],
+ "des": "进驻发电站时,如果其他<@cc.kw>发电站>内没有进驻<$cc.tag.op><@cc.kw>作业平台>>,则<@cc.kw>发电站>额外<@cc.vup>+1>(仅影响设施数量)",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_count",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 240,
+ "name": "多萝西",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "源石技艺理论应用",
+ "buffer": true,
+ "buffer_des": [
+ "cc_sk_manu2"
+ ],
+ "des": "进驻制造站时,当前制造站内每个<$cc.sk.manu2><@cc.kw>莱茵科技类技能>>为自身<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_skill_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "莱茵科技·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 239,
+ "name": "车尔尼",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "慢板行歌",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1_a3"
+ ],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.65>(同种效果取最高),同时当前宿舍每级提供<@cc.vup>1>个<$cc.bd_a1_a3><@cc.rem>小节>>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&bd_n1_n3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "琴键漫步",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1",
+ "cc_bd_a1_a3"
+ ],
+ "des": "进驻宿舍时,每<@cc.vup>1>个<$cc.bd_a1_a3><@cc.rem>小节>>转化为<@cc.vup>1>点<$cc.bd_a1><@cc.rem>感知信息>>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&bd_n1_2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 238,
+ "name": "濯尘芙蓉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "异格者",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sp"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.sp><@cc.kw>异格>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_sp",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "护理专精",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 237,
+ "name": "黑键",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "乐感",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B",
+ "cc_bd_a1"
+ ],
+ "des": "进驻贸易站时,宿舍内每有<@cc.kw>1>名干员则<$cc.bd_a1><@cc.rem>感知信息>><@cc.vup>+1>,同时每<@cc.vup>1>点<$cc.bd_a1><@cc.rem>感知信息>>转化为<@cc.vup>1>点<$cc.bd_B><@cc.rem>无声共鸣>>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_bd_n1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "徘徊旋律",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B"
+ ],
+ "des": "进驻贸易站时,每<@cc.vup>4>点<$cc.bd_B><@cc.rem>无声共鸣>><@cc.vup>+1%>订单效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd_bd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "怅惘和声",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_B"
+ ],
+ "des": "进驻贸易站时,每<@cc.vup>2>点<$cc.bd_B><@cc.rem>无声共鸣>><@cc.vup>+1%>订单效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd_bd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 236,
+ "name": "埃拉托",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "拂弦",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 235,
+ "name": "掠风",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "必修课程",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "自动化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 234,
+ "name": "流明",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "柔和微光·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>,同时每有<@cc.kw>1>间发电站,则恢复效果额外<@cc.vup>+0.05>(叠加后的最终值同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_powToRecAll1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "柔和微光·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>,同时每有<@cc.kw>1>间发电站,则恢复效果额外<@cc.vup>+0.05>(叠加后的最终值同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_powToRecAll2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 233,
+ "name": "艾丽妮",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "剑与手炮",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>与<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard&sniper",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "精神锻炼",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室单次协助一名干员训练时长达<@cc.vup>5>小时,则该名干员下次训练所需时间<@cc.vup>-50%>(任意一方离开训练室时,效果消失)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_reduceTime",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 232,
+ "name": "归溟幽灵鲨",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特种专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "阿戈尔战术",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+65%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_specialist2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "斗争渴望",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助<@cc.kw>特种>干员训练专精技能至<@cc.vup>3>级时,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specter",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 231,
+ "name": "褐果",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "地质学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 230,
+ "name": "海蒂",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "名流欢会",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+35%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd3",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“一千封信”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,会客室更容易获得<@cc.kw>格拉斯哥帮>线索(工作时长影响概率)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_victoria",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 229,
+ "name": "洛洛",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "舍弃的赘余",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>65%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p4",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "无用的赠予",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>产出<@cc.kw>T3>品质的副产品时,副产品必定为<@cc.kw>聚酸酯组>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_drop_polyester",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 228,
+ "name": "号角",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "军事工程学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "DIY·炽合金",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>炽合金块>材料时,副产品的产出概率提升<@cc.vup>100%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_alloyBlock",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 227,
+ "name": "风丸",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "化影",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>15%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "得心应手",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,如果会客室内只有自身处于工作状态时,线索搜集速度提升<@cc.vup>35%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_condChar",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 226,
+ "name": "见行者",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "逻辑推理",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>35%>,心情每小时消耗<@cc.vdown>+2>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd&cost",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 225,
+ "name": "菲亚梅塔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "自律",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+2>,同时<@cc.vdown>无法获得>其他来源提供的心情恢复效果",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_reborn",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "患难之交",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,如果自身为<@cc.kw>满心情>,则与当前宿舍<@cc.kw>前一位>进驻的干员互换心情",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_exchangeAp",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 224,
+ "name": "夏栎",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "风声",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得<@cc.kw>格拉斯哥帮>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_glasgow1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "情报整理",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室后,每当新搜集到的线索不是<@cc.kw>格拉斯哥帮>时,则额外增加<@cc.kw>格拉斯哥帮>线索的出现概率(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_flag_glasgow",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 223,
+ "name": "澄闪",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "电磁充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "电荷释放",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd3",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 222,
+ "name": "夜半",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "先锋专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "战术研习",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_vanguard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 221,
+ "name": "寒芒克洛丝",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "异格者",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sp"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.sp><@cc.kw>异格>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_sp",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:速射",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>速射手>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_fastshot",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 220,
+ "name": "老鲤",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "浮生得闲",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>阿>进驻控制中枢一起工作时,控制中枢内所有干员心情每小时恢复<@cc.vup>+0.25>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_lda",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "世事洞明",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,会客室线索搜集速度<@cc.vup>+25%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 219,
+ "name": "令",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "杯莫停",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sui"
+ ],
+ "des": "进驻控制中枢时,<@cc.kw>消除>当前控制中枢内所有<$cc.g.sui><@cc.kw>岁>>干员<@cc.kw>自身>心情消耗的影响",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_clear_sui",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“山河远阔”",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1",
+ "cc_bd_b1"
+ ],
+ "des": "进驻控制中枢时,当自身心情大于<@cc.kw>12>时,<$cc.bd_b1><@cc.rem>人间烟火>><@cc.vup>+15>;当自身心情处于<@cc.kw>12>以下时,<$cc.bd_a1><@cc.rem>感知信息>><@cc.vup>+10>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_bd1&bd2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 218,
+ "name": "暮落",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "重装专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "实战技巧:驭法铁卫",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>,如果训练位干员分支为<@cc.kw>驭法铁卫>,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_artsprotector",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 217,
+ "name": "九色鹿",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "因果",
+ "buffer": true,
+ "buffer_des": [
+ "cc_w_ncdeer1"
+ ],
+ "des": "进驻加工站时,累积<@cc.kw>40>点<$cc.w.ncdeer1><@cc.rem>因果>>必定产出一次副产品",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_bonus1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "业报",
+ "buffer": true,
+ "buffer_des": [
+ "cc_w_ncdeer2"
+ ],
+ "des": "进驻加工站时,累积<@cc.kw>80>点<$cc.w.ncdeer2><@cc.rem>业报>>必定产出一次副产品",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_bonus2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 216,
+ "name": "耶拉",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "雪境守望",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>喀兰贸易>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_kjerag2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "耶拉冈德",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室后,每当新搜集到的线索不是<@cc.kw>喀兰贸易>时,则额外增加<@cc.kw>喀兰贸易>线索的出现概率(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_flag_kjerag",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 215,
+ "name": "极光",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "重装专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "极地生存",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 214,
+ "name": "灵知",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "幕后指挥",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_karlan"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.karlan><@cc.kw>喀兰贸易>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_karlan",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "精密计算",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_karlan"
+ ],
+ "des": "进驻控制中枢时,每个进驻在贸易站的<$cc.g.karlan><@cc.kw>喀兰贸易>>干员,订单获取效率<@cc.vdown>-15%>,订单上限<@cc.vup>+6>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_t_limit&spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 213,
+ "name": "蚀清",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "腐蚀科学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "腐蚀科学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "缓蚀技术",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>产出<@cc.kw>T3>品质的副产品时,副产品必定为<@cc.kw>异铁组>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_drop_oriron",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 212,
+ "name": "布丁",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "设备维护",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "超频",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_op"
+ ],
+ "des": "进驻控制中枢时,如果有<@cc.kw>2>台以上<$cc.tag.op><@cc.kw>作业平台>>进驻在<@cc.kw>发电站>,则所有制造站生产力<@cc.vup>+2%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_token_p_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 211,
+ "name": "蜜莓",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "草药学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "草药学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "心理疏导",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.7>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 210,
+ "name": "正义骑士号",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "备用能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "“滴滴,启动!”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,<@cc.kw>野鬃>所在的制造站生产力<@cc.vup>+5%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_jnight",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 209,
+ "name": "野鬃",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "红松骑士团·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "红松骑士团·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 208,
+ "name": "焰尾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "小小的领袖",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "红松的骑士",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_psk"
+ ],
+ "des": "进驻控制中枢时,每个进驻在制造站的<$cc.g.psk><@cc.kw>红松骑士团>>干员,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+10%>,<@cc.kw>贵金属>类配方的生产力<@cc.vdown>-10%>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_psk",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 207,
+ "name": "耀骑士临光",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "骑士训练",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_knight"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.tag.knight><@cc.kw>骑士>>干员为当前干员的专精技能训练速度<@cc.vup>+5%>(最多生效<@cc.kw>5>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_knight_bd1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "崇高准则",
+ "buffer": true,
+ "buffer_des": [
+ "cc_tag_knight"
+ ],
+ "des": "进驻训练室协助位时,基建内(不包含副手)每名<$cc.tag.knight><@cc.kw>骑士>>干员为当前干员的专精技能训练速度<@cc.vup>+5%>(最多生效<@cc.kw>8>名)",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_knight_bd2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 206,
+ "name": "灰毫",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "红松骑士团·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "红松骑士团·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 205,
+ "name": "远牙",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“归乡”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.55>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "红松骑士团·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "红松骑士团·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 204,
+ "name": "桑葚",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "救援队·珠算",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+10%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost1",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "救援队·资源清点",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&cost2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "救援队·灾后普查",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻人力办公室时,每个招募位(不包含初始招募位)<@cc.vup>+10>点<<$cc.bd_b1><@cc.rem>人间烟火>>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd_bd_n2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 203,
+ "name": "罗比菈塔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "工程学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 202,
+ "name": "琴柳",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "维多利亚文学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.7>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "感染力",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,人力办公室联络速度小于<@cc.vup>30%>时(其中包含基础联络速度5%),则联络速度额外<@cc.vup>+20%>(该加成全局效果唯一,不受其它加成影响)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_h_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 201,
+ "name": "龙舌兰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "投资·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站后,如果下笔<@cc.kw>赤金订单>交付数大于<@cc.kw>3>(违约订单不视作赤金订单),则其龙门币收益<@cc.kw>+250>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_long1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "投资·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站后,如果下笔<@cc.kw>赤金订单>交付数大于<@cc.kw>3>(违约订单不视作赤金订单),则其龙门币收益<@cc.kw>+500>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_long2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 200,
+ "name": "羽毛笔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "放空",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "适应力",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 199,
+ "name": "水月",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "意识协议",
+ "buffer": true,
+ "buffer_des": [
+ "cc_sk_manu1"
+ ],
+ "des": "进驻制造站时,当前制造站内每个<$cc.sk.manu1><@cc.kw>标准化类技能>>为自身<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_skill_spd",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 198,
+ "name": "假日威龙陈",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "以身作则",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+65%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_sniper2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "工作狂",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助<@cc.kw>狙击>干员训练专精技能至<@cc.vup>1>级时,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_chen",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 197,
+ "name": "帕拉斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "智慧之境",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "胜利之计",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 196,
+ "name": "深靛",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "毒剂师之友",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高),如果目标是<@cc.kw>蓝毒>,则恢复效果额外<@cc.vup>+0.45>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single_indigo",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "光能充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "灯塔供能模块",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 195,
+ "name": "贝娜",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“可靠”助手",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vdown>-20%>,仓库容量上限<@cc.vup>+17>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit&cost4",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "裁缝·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 194,
+ "name": "绮良",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单流可视化·α",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+5%>,每有<@cc.kw>4>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>,则为当前贸易站额外提供<@cc.vup>2>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_gc1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "订单流可视化·β",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+5%>,每有<@cc.kw>2>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>,则为当前贸易站额外提供<@cc.vup>2>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_gc2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 193,
+ "name": "卡涅利安",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "术师专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "尽在掌握",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 192,
+ "name": "赤冬",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "信影流",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 191,
+ "name": "歌蕾蒂娅",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "潮汐守望",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_abyssal"
+ ],
+ "des": "进驻控制中枢时,每有1个<$cc.g.abyssal><@cc.kw>深海猎人>>干员进驻在宿舍以外的设施,则自身心情每小时消耗<@cc.vdown>+0.5>;反之则自身心情每小时恢复<@cc.vup>+0.5>,如果进驻在宿舍内的<$cc.g.abyssal><@cc.kw>深海猎人>>干员为满心情,则额外<@cc.vup>+0.5>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_aegir",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "集群狩猎·α",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_abyssal2_1",
+ "cc_c_abyssal2_3",
+ "cc_g_abyssal"
+ ],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>;基建内<$cc.g.abyssal><@cc.kw>深海猎人>>干员获得<$cc.c.abyssal2_1><@cc.rem>特殊加成>>(与部分技能有<$cc.c.abyssal2_3><@cc.rem>特殊叠加规则>>)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_aegir",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "集群狩猎·β",
+ "buffer": true,
+ "buffer_des": [
+ "cc_c_abyssal2_2",
+ "cc_c_abyssal2_3",
+ "cc_g_abyssal"
+ ],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>;基建内<$cc.g.abyssal><@cc.kw>深海猎人>>干员获得<$cc.c.abyssal2_2><@cc.rem>特殊加成>>(与部分技能有<$cc.c.abyssal2_3><@cc.rem>特殊叠加规则>>)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_aegir2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 190,
+ "name": "凯尔希",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "未知技术",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p5",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "最高权限",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有制造站生产力<@cc.vup>+2%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_p_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 189,
+ "name": "浊心斯卡蒂",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "辅助专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_supporter1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "同化",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+65%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_supporter2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "变异",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助<@cc.kw>辅助>干员训练专精技能至<@cc.vup>3>级时,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_skadi",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 188,
+ "name": "暴雨",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "护卫",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "重装专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "阵地经验",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 187,
+ "name": "熔泉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "熔铸",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "DIY·异铁",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>异铁>类材料时,副产品的产出概率提升<@cc.vup>90%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_oriron",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 186,
+ "name": "异客",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "电磁充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "聚能",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "自动化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 185,
+ "name": "战车",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "彩虹小队",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_R6"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.R6><@cc.kw>彩虹小队>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_r6",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "乌萨斯特饮",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_tachanka",
+ "cc_g_ussg"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每有1名<$cc.g.ussg><@cc.kw>乌萨斯学生自治团>>干员,则<$cc.bd_tachanka><@cc.rem>乌萨斯特饮>><@cc.vup>+1>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_tachanka",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 184,
+ "name": "霜华",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "彩虹小队",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_R6"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.R6><@cc.kw>彩虹小队>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_r6",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "机械工程",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_ash",
+ "cc_bd_tachanka"
+ ],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>,每<@cc.vup>1>点<$cc.bd_ash><@cc.rem>情报储备>>额外提升<@cc.vup>5%>;如果<$cc.bd_tachanka><@cc.rem>乌萨斯特饮>>达到<@cc.vup>4>瓶,则额外提升<@cc.vup>15%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_frost",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 183,
+ "name": "闪击",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "彩虹小队",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_R6"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.R6><@cc.kw>彩虹小队>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_r6",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "语言学",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_ash",
+ "cc_bd_tachanka"
+ ],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,每<@cc.vup>1>点<$cc.bd_ash><@cc.rem>情报储备>>额外<@cc.vup>+5%>联络速度,每<@cc.vup>1>瓶<$cc.bd_tachanka><@cc.rem>乌萨斯特饮>>额外<@cc.vup>+5%>联络速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_blitz",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 182,
+ "name": "灰烬",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "彩虹小队",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_R6"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.R6><@cc.kw>彩虹小队>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_r6",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "情报储备",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_ash",
+ "cc_g_R6"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每有1名<$cc.g.R6><@cc.kw>彩虹小队>>干员,则<$cc.bd_ash><@cc.rem>情报储备>><@cc.vup>+1>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_ash",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 181,
+ "name": "图耶",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "物流规划·α",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+5%>,每有<@cc.kw>4>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>,则当前贸易站订单获取效率额外<@cc.vup>+15%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_gs1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "物流规划·β",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_flow_gold"
+ ],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+5%>,每有<@cc.kw>2>条<$cc.t.flow_gold><@cc.kw>赤金生产线>>,则当前贸易站订单获取效率额外<@cc.vup>+15%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_flow_gs2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 180,
+ "name": "炎狱炎熔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "异格者",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_sp"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.sp><@cc.kw>异格>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_sp",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "热能充能·γ",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd3",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 179,
+ "name": "乌有",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "好事之徒",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+35%>,同时每个招募位(不包含初始招募位)额外<@cc.vup>+5%>会客室线索搜集速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&clue",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“愿者上钩”",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻贸易站时,宿舍内每有<@cc.kw>1>名干员则<$cc.bd_b1><@cc.rem>人间烟火>><@cc.vup>+1>,同时每有<@cc.vup>1>点<$cc.bd_b1><@cc.rem>人间烟火>>,则订单获取效率<@cc.vup>+1%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_bd_n2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 178,
+ "name": "嵯峨",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "先锋专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "入世",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 177,
+ "name": "夕",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "\"不以物喜\"",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_b1"
+ ],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>;当自身心情处于<@cc.kw>12>以下时,<$cc.bd_b1><@cc.rem>人间烟火>><@cc.vup>+15>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_bd1",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "\"不以己悲\"",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1"
+ ],
+ "des": "进驻控制中枢时,自身心情每小时消耗<@cc.vdown>+0.5>;当自身心情大于<@cc.kw>12>时,<$cc.bd_a1><@cc.rem>感知信息>><@cc.vup>+10>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost_bd2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 176,
+ "name": "豆苗",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "磐蟹·豆豆",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "磐蟹·阿盘",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 175,
+ "name": "爱丽丝",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "睡前故事",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1_a2"
+ ],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高),同时当前宿舍每级提供<@cc.vup>1>层<$cc.bd_a1_a2><@cc.rem>梦境>>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&bd_n1_n2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "梦境呓语",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1",
+ "cc_bd_a1_a2"
+ ],
+ "des": "进驻宿舍时,每<@cc.vup>1>层<$cc.bd_a1_a2><@cc.rem>梦境>>转化为<@cc.vup>1>点<$cc.bd_a1><@cc.rem>感知信息>>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&bd_n1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 174,
+ "name": "空弦",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "虔诚筹款·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,每间宿舍每级<@cc.vup>+1%>获取效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&dorm1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "虔诚筹款·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,每间宿舍每级<@cc.vup>+2%>获取效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&dorm2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 173,
+ "name": "松果",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "工程学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "便携蓄电池",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build_cost",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 172,
+ "name": "罗宾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特种专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "陷阱对焦",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_specialist1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 171,
+ "name": "卡夫卡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "手工艺品·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "手工艺品·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 170,
+ "name": "山",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "内幕",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,每产生<@cc.vup>1>次联络次数,增加<@cc.kw>黑钢国际>线索的概率(工作时长和招募位影响概率)",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&blacksteel2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "哥伦比亚史",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室后,每当新搜集到的线索不是<@cc.kw>莱茵生命>时,则额外增加<@cc.kw>莱茵生命>线索的出现概率(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_flag_rhine",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 169,
+ "name": "杰克",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "活泼",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.2>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.4>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one01",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 168,
+ "name": "絮雨",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "巡游",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1_a1"
+ ],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,同时每个招募位(不包含初始招募位)<@cc.vup>+10>点<$cc.bd_a1_a1><@cc.rem>记忆碎片>>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd_bd_n1_n1",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "追忆",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_a1",
+ "cc_bd_a1_a1"
+ ],
+ "des": "进驻人力办公室时,每<@cc.vup>1>点<$cc.bd_a1_a1><@cc.rem>记忆碎片>>转化为<@cc.vup>1>点<$cc.bd_a1><@cc.rem>感知信息>>,心情耗尽时清空所有<$cc.bd_a1_a1><@cc.rem>记忆碎片>>和自身累积的<$cc.bd_a1><@cc.rem>感知信息>>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd_memento",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 167,
+ "name": "泥岩",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "执著",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,相应配方的心情消耗恒定为<@cc.vup>2>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_constant",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "DIY·源岩",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>源岩>类材料时,副产品的产出概率提升<@cc.vup>90%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_orirock",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 166,
+ "name": "迷迭香",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "超感",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_A",
+ "cc_bd_a1"
+ ],
+ "des": "进驻制造站时,宿舍内每有<@cc.kw>1>名干员则<$cc.bd_a1><@cc.rem>感知信息>><@cc.vup>+1>,同时每<@cc.vup>1>点<$cc.bd_a1><@cc.rem>感知信息>>转化为<@cc.vup>1>点<$cc.bd_A><@cc.rem>思维链环>>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd_n1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "念力",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_A"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>2>点<$cc.bd_A><@cc.rem>思维链环>><@cc.vup>+1%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "意识实体",
+ "buffer": true,
+ "buffer_des": [
+ "cc_bd_A"
+ ],
+ "des": "进驻制造站时,每<@cc.vup>1>点<$cc.bd_A><@cc.rem>思维链环>><@cc.vup>+1%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_bd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 165,
+ "name": "鞭刃",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "教官",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,干员的专精技能训练速度<@cc.vup>+25%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_all",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 164,
+ "name": "泡泡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "囤积者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+10>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "大就是好!",
+ "buffer": true,
+ "buffer_des": [
+ "cc_m_var1"
+ ],
+ "des": "进驻制造站时,当前制造站内干员根据自身提升的仓库容量提供一定的生产力。提升<@cc.vup>16>格以下的,每格仓库容量提供<@cc.vup>1%>生产力;提升大于<@cc.vup>16>格的,每格仓库容量提供<@cc.vup>3%>生产力(无法与<$cc.m.var1><@cc.rem>回收利用>>叠加,且优先生效)",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_variable31",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 163,
+ "name": "奥斯塔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "灵感",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "DIY·聚酸酯",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>聚酸酯>类材料时,副产品的产出概率提升<@cc.vup>90%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_polyester",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 162,
+ "name": "瑕光",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "精打细算",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,减免龙门币消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_free",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "热心修补匠",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>40%>,同时心情消耗为<@cc.kw>8>以上的配方全部<@cc.vup>-4>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_cost_blemishine",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 161,
+ "name": "薄绿",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "术师专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "博览群书",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "地质学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 160,
+ "name": "芳汀",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“物尽其用”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,宿舍内每有<@cc.kw>1>名心情<@cc.vup>4>以下的干员,副产品的产出概率提升<@cc.vup>5%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_dorm1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "“人善其事”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,宿舍内每有<@cc.kw>1>名心情<@cc.vup>20>以下的干员,副产品的产出概率提升<@cc.vup>5%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_dorm3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "“独处”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>;如果该宿舍内有其他干员,则每人额外为自身心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 159,
+ "name": "四月",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单管理·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "订单管理·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,且订单上限<@cc.vup>+4>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "鼓舞",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 158,
+ "name": "史尔特尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "剑术记忆",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 157,
+ "name": "酸糖",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "B-girl",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "Give me five",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.35>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.35>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one12",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 156,
+ "name": "燧石",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "苦行",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>2>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train2_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 155,
+ "name": "特米米",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "烤肉大师",
+ "buffer": true,
+ "buffer_des": [
+ "cc_gvial"
+ ],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高),如果目标是<@cc.kw><$cc.gvial>嘉维尔>>,则恢复效果额外<@cc.vup>+0.45>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single_tomimi",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "一知半解",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>1>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train1_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 154,
+ "name": "森蚺",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "自动化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+5%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "自动化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+10%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "我寻思能行",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,如果<@cc.kw>Lancet-2>进驻在<@cc.kw>发电站>,<@cc.kw>发电站>额外<@cc.vup>+2>(仅影响设施数量)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_p_bot",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 153,
+ "name": "孑",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "摊贩经济",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前订单数与订单上限每差<@cc.vup>1>笔订单,则订单获取效率<@cc.vup>+4%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit_diff",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "市井之道",
+ "buffer": true,
+ "buffer_des": [
+ "cc_t_strong2"
+ ],
+ "des": "进驻贸易站时,当前贸易站内其他干员提供的每<@cc.vup>10%>订单获取效率使订单上限<@cc.vdown>-1>(订单最少为1),同时每有<@cc.vup>1>笔订单就<@cc.vup>+4%>订单获取效率(与部分技能有<$cc.t.strong2><@cc.rem>特殊叠加规则>>)",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit_count",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 152,
+ "name": "安哲拉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "麻烦回避者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.85>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one4",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 151,
+ "name": "棘刺",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“炼金术”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“爆炸艺术”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,每<@cc.vup>2>次加工没有产出副产品,则<@cc.kw>恢复自身一次心情>,恢复量为<@cc.kw>对应配方所需消耗的心情>(加工结束后统一结算)",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_recovery",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 150,
+ "name": "贾维",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "灵感",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "DIY·装置",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>装置>类材料时,副产品的产出概率提升<@cc.vup>90%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_device",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 149,
+ "name": "蜜蜡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "术师专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "术师专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 148,
+ "name": "稀音",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "延时摄影",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站后,生产力首小时<@cc.vup>+15%>,此后每小时<@cc.vup>+2%>,最终达到<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "剪辑·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产<@cc.kw>作战记录>类配方时,仓库容量上限<@cc.vup>+12>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp&limit1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 147,
+ "name": "卡达",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "Vlog",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产<@cc.kw>作战记录>类配方时,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp&cost",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "剪辑·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产<@cc.kw>作战记录>类配方时,仓库容量上限<@cc.vup>+15>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp&limit2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 146,
+ "name": "亚叶",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "毒理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "一丝不苟",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 145,
+ "name": "断崖",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 144,
+ "name": "铃兰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "辅助专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_supporter1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "共鸣",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_supporter3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 143,
+ "name": "波登可",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "疗养",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.65>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "沁人心脾",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 142,
+ "name": "莱恩哈特",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "结构力学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "选矿学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-2>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build_cost2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 141,
+ "name": "苦艾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "秘密搜查",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得<@cc.kw>乌萨斯学生自治团>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_ursus1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "甄别",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室后,每当新搜集到的线索不是<@cc.kw>乌萨斯学生自治团>时,则额外增加<@cc.kw>乌萨斯学生自治团>线索的出现概率(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_flag_ursus",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 140,
+ "name": "早露",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "学生会会长",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_ussg"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.ussg><@cc.kw>乌萨斯学生自治团>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_ussg",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "人望",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>,每产生<@cc.vup>1>次联络次数,增加<@cc.kw>乌萨斯学生自治团>线索的概率(工作时长和招募位影响概率)",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&ursus2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 139,
+ "name": "石棉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特立独行",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>,仓库容量上限<@cc.vdown>-12>,心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit&cost3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "探险者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+16>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 138,
+ "name": "月禾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天灾信使·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "洞悉人心",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+35%>,同时每个招募位(不包含初始招募位)额外<@cc.vup>+5%>会客室线索搜集速度",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd&clue",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 137,
+ "name": "THRM-EX",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "备用能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "热情澎湃",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,心情每小时消耗<@cc.vup>-0.52>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd&cost",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 136,
+ "name": "极境",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "通讯员",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得<@cc.kw>罗德岛制药>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_rhodes1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "插旗",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室后,每当新搜集到的线索不是<@cc.kw>罗德岛制药>时,则额外增加<@cc.kw>罗德岛制药>线索的出现概率(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_flag_rhodes",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 135,
+ "name": "温蒂",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "自动化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+10%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "仿生海龙",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的生产力<@cc.vdown>全部归零>(不包含根据设施数量提供加成的生产力),每个<@cc.kw>发电站>为当前制造站<@cc.vup>+15%>的生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&power3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 134,
+ "name": "W",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "伺机而动",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+65%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_sniper2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "索然无味",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助<@cc.kw>狙击>干员训练专精技能至<@cc.vup>3>级时,心情每小时消耗<@cc.vdown>+1>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_w",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 133,
+ "name": "铸铁",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "尽心尽力",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>,如果本次训练专精技能至<@cc.vup>3>级,则训练速度额外<@cc.vup>+45%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train3_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 132,
+ "name": "刻刀",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特训记录",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>芯片>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_asc1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "淡泊",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>芯片>时,心情消耗为<@cc.kw>2>的配方全部<@cc.vup>-1>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_asc_cost1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 131,
+ "name": "巫恋",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "裁缝·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "低语",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站内其他干员提供的订单获取效率<@cc.vdown>全部归零>,且每人为自身<@cc.vup>+45%>订单获取效率,同时全体心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_vodfox",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 130,
+ "name": "傀影",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特种专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "假面魅影",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 129,
+ "name": "断罪者",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天启",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.7>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "拳术指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 128,
+ "name": "惊蛰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "至察",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,<@cc.kw>小幅提升>会客室内干员<@cc.kw>所属派系的线索倾向效果>,但控制中枢内所有干员的心情每小时消耗<@cc.vdown>+0.5>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_wt1",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "断事如神",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,<@cc.kw>小幅提升>会客室内干员<@cc.kw>所属派系的线索倾向效果>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_wt2",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 127,
+ "name": "刻俄柏",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "“都想要”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "“等不及”",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站后,生产力首小时<@cc.vup>+20%>,此后每小时<@cc.vup>+1%>,最终达到<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 126,
+ "name": "清流",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "清洁能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "再生能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,每个<@cc.kw>贸易站>为当前制造站<@cc.kw>贵金属>类配方的生产力<@cc.vup>+20%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&trade",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 125,
+ "name": "柏喙",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "裁缝·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>小幅提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "裁缝·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,<@cc.kw>提升>当前贸易站<@cc.kw>高品质贵金属订单>的出现概率(工作时长影响概率),心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_wt&cost2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 124,
+ "name": "宴",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "心理学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 123,
+ "name": "慑砂",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "极简实用主义",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,心情消耗为<@cc.kw>4>的配方全部<@cc.vup>-2>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve_cost2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "工业设计",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 122,
+ "name": "风笛",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "牧歌",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.55>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "训练有素",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>芯片>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_asc2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 121,
+ "name": "雪雉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天道酬勤·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站内干员提供的每<@cc.vup>5%>订单获取效率,额外提供<@cc.vup>5%>效率,最多提供<@cc.vup>25%>效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd_variable21",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "天道酬勤·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,当前贸易站内干员提供的每<@cc.vup>5%>订单获取效率,额外提供<@cc.vup>5%>效率,最多提供<@cc.vup>35%>效率",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd_variable22",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 120,
+ "name": "吽",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "能工巧匠",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "坚毅随和",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_lda"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.lda><@cc.kw>鲤氏侦探事务所>>干员可使该派系干员<@cc.kw>额外恢复心情>,同时每人可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.2>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_lda_add",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 119,
+ "name": "阿",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "神经质",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,<@cc.kw>提升>会客室内干员<@cc.kw>所属派系的线索倾向效果>,但控制中枢内所有干员的心情每小时消耗<@cc.vdown>+1.5>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_c_wt",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "精准手术",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 118,
+ "name": "年",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "稀有金属辨识",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>100%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve4",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "肆无忌惮",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,相应配方全部<@cc.vdown>+2>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_nian",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 117,
+ "name": "安比尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "慵懒",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vdown>-0.1>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 116,
+ "name": "灰喉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "必要责任",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 115,
+ "name": "煌",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "热能充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "斩铁裂钢",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 114,
+ "name": "布洛卡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "近卫专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 113,
+ "name": "苇草",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "替身",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 112,
+ "name": "梅",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "皇家探员(自称)",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,更容易获得<@cc.kw>企鹅物流>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_penguin1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 111,
+ "name": "槐琥",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "团队精神",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>消除>当前制造站内所有干员<@cc.kw>自身>心情消耗的影响",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_cost_all",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "配合意识",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内其他干员提供的每<@cc.vup>5%>生产力(不包含根据设施数量提供加成的生产力),额外提供<@cc.vup>5%>生产力,最多提供<@cc.vup>40%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_variable21",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 110,
+ "name": "拜松",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "峯驰物流",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "少当家",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit7",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 109,
+ "name": "莫斯提马",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "信使·企鹅物流",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>企鹅物流>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_penguin2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 108,
+ "name": "伊桑",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "WRITER",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "隐形的美食家",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.75>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 107,
+ "name": "微风",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "没落贵族",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>格拉斯哥帮>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_glasgow2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 106,
+ "name": "红云",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "拾荒者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "回收利用",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,当前制造站内干员提升的每格仓库容量,提供<@cc.vup>2%>生产力",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_variable11",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 105,
+ "name": "送葬人",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "清理协议",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 104,
+ "name": "炎客",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "技巧理论",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "登峰造极",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 103,
+ "name": "麦哲伦",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "多功能测绘仪",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "龙腾式无人机",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,心情消耗为<@cc.kw>4>以上的配方全部<@cc.vup>-2>心情消耗",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_cost_magallan",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 102,
+ "name": "坚雷",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "重装专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "零食网络",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 101,
+ "name": "星极",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "星象学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>25%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 100,
+ "name": "桃金娘",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "谈判",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单上限<@cc.vup>+5>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "领袖",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 99,
+ "name": "赫拉格",
+ "span": 4,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "超脱",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.55>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "挣脱",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内除自身以外所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 2,
+ "phase_level": "精2 1级",
+ "skillname": "解脱",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.55>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "兵者诡道",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 98,
+ "name": "苏苏洛",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "医疗专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "药理学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 97,
+ "name": "格劳克斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "电磁充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "电磁充能·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 96,
+ "name": "锡兰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "学者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "源石研究",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 95,
+ "name": "黑",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "沏茶",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高),如果目标是<@cc.kw>锡兰>,则恢复效果额外<@cc.vup>+0.45>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single_schwarz",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "黑矢",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 94,
+ "name": "安洁莉娜",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "信使·罗德岛制药",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>罗德岛制药>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_rhodes2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 93,
+ "name": "斯卡蒂",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "悲歌",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+1>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one5",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 92,
+ "name": "塞雷娅",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "守望者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>莱茵生命>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_rhine2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 91,
+ "name": "艾雅法拉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "火山学家",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "天灾信使·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+45%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd5",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 90,
+ "name": "夜莺",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "鼓舞",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.1>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "提灯女神",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 89,
+ "name": "银灰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "喀兰贸易·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+15%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit3",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "喀兰之主",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>,且订单上限<@cc.vup>+4>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit5",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 88,
+ "name": "闪灵",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "慈悲",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.75>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single4",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 87,
+ "name": "星熊",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "重装专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "般若",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+60%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender3",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 86,
+ "name": "伊芙利特",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "热能充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "高热充能",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 85,
+ "name": "推进之王",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "领袖",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "狮心王",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 84,
+ "name": "能天使",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "企鹅物流·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "物流专家",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+35%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd3",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 83,
+ "name": "诗怀雅",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "大小姐",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有贸易站订单效率<@cc.vup>+7%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_t_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "教官",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,干员的专精技能训练速度<@cc.vup>+25%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_all",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 82,
+ "name": "陈",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "德才兼备",
+ "buffer": true,
+ "buffer_des": [
+ "cc_g_lgd"
+ ],
+ "des": "进驻控制中枢时,控制中枢内每个<$cc.g.lgd><@cc.kw>龙门近卫局>>干员可使控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_lungmen",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "警司",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>25%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 81,
+ "name": "梅尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "咪波·加工型",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>65%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p4",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "咪波·制造型",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 80,
+ "name": "食铁兽",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "作战指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "拳术指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 79,
+ "name": "暴行",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "技巧理论",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "烹饪",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.35>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.35>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one12",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 78,
+ "name": "格拉尼",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "先锋专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "先锋专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 77,
+ "name": "陨星",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "爆破材料学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "爆破材料学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 76,
+ "name": "狮蝎",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "特种专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "特种专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>特种>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_specialist2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 75,
+ "name": "白金",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "狙击专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "狙击专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>狙击>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_sniper2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 74,
+ "name": "可颂",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "企鹅物流·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "使命必达",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit7",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 73,
+ "name": "真理",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "辅助专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_supporter1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "军师",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>乌萨斯学生自治团>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_ursus2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 72,
+ "name": "初雪",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "喀兰圣女",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.5>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.25>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one22",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "辅助专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>辅助>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_supporter2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 71,
+ "name": "崖心",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "探险家的热情",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.25>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.5>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one02",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "喀兰贸易·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+15%>,且订单上限<@cc.vup>+4>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit4",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 70,
+ "name": "华法琳",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "医疗专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "医疗专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 69,
+ "name": "天火",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "术师专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "术师专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 68,
+ "name": "夜魔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "术师专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>术师>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_caster1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "心理学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 67,
+ "name": "火神",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "工匠精神·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vdown>-5%>,仓库容量上限<@cc.vup>+16>,心情每小时消耗<@cc.vup>-0.15>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "工匠精神·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vdown>-5%>,仓库容量上限<@cc.vup>+19>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit&cost2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 66,
+ "name": "守林人",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 65,
+ "name": "因陀罗",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "传讯者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>格拉斯哥帮>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_glasgow2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 64,
+ "name": "临光",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "左膀右臂",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "使徒",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.5>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.25>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one22",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 63,
+ "name": "普罗旺斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天灾信使·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "天灾信使·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+45%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd5",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 62,
+ "name": "红",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "S.W.E.E.P.",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "追踪者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>25%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 61,
+ "name": "幽灵鲨",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "狂热",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.85>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one4",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 60,
+ "name": "拉普兰德",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "醉翁之意·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>德克萨斯>在同一个贸易站时,心情每小时消耗<@cc.vup>-0.1>,订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_Lappland1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "醉翁之意·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>德克萨斯>在同一个贸易站时,心情每小时消耗<@cc.vup>-0.1>,订单上限<@cc.vup>+4>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_Lappland2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 59,
+ "name": "蓝毒",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "毒理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "毒理学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 58,
+ "name": "白面鸮",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "莱茵科技·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "莱茵科技·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 57,
+ "name": "凛冬",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "领袖",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "冬将军",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all3",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 56,
+ "name": "赫默",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "莱茵科技·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "莱茵科技·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 55,
+ "name": "雷蛇",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "脉冲电弧·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "脉冲电弧·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd3",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 54,
+ "name": "芙兰卡",
+ "span": 4,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "近卫专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd1",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 1,
+ "phase_level": "精2 1级",
+ "skillname": "B.P.R.S.",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>黑钢国际>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_blacksteel2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 53,
+ "name": "德克萨斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "恩怨",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>拉普兰德>在同一个贸易站时,心情每小时消耗<@cc.vdown>+0.3>,订单获取效率<@cc.vup>+65%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_texas1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "默契",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "当与<@cc.kw>能天使>在同一个贸易站时,心情每小时消耗<@cc.vup>-0.3>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_texas2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 52,
+ "name": "空",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "偶像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "企鹅物流·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 51,
+ "name": "阿米娅",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "合作协议",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,所有贸易站订单效率<@cc.vup>+7%>(同种效果取最高)",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_t_spd",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精2 1级",
+ "skillname": "小提琴独奏",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.15>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 50,
+ "name": "格雷伊",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "静电场",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+20%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd3",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "能工巧匠",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 49,
+ "name": "红豆",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "作战指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "先锋专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 48,
+ "name": "缠丸",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "技巧理论",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>技巧概要>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_skill1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 47,
+ "name": "阿消",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "工程学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>基建材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_build1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "设备维护",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+15%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd2",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 46,
+ "name": "砾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "专注·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>40%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "金属工艺·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 45,
+ "name": "暗索",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "谈判",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单上限<@cc.vup>+5>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 44,
+ "name": "杰西卡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "联络员",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>黑钢国际>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_blacksteel2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 43,
+ "name": "角峰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "喀兰贸易·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+15%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit3",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "重装专精·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>重装>干员的专精技能训练速度<@cc.vup>+50%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_defender2",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 42,
+ "name": "讯使",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "喀兰贸易·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+15%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit3",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "讯使",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>10%>,且更容易获得<@cc.kw>喀兰贸易>线索(工作时长影响概率)",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_kjerag2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 41,
+ "name": "古米",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "交际",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "烹饪",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.35>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.35>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one12",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 40,
+ "name": "霜叶",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "作战指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 39,
+ "name": "嘉维尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "药理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "医疗专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>医疗>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_medic1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 38,
+ "name": "慕斯",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "烘焙",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.3>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.3>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one11",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 37,
+ "name": "地灵",
+ "span": 3,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "天灾信使·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+30%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd2",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精1 1级",
+ "skillname": "准时下班",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+45%>,心情每小时消耗<@cc.vdown>+2>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_skgoat",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "地质学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 36,
+ "name": "调香师",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "药理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 35,
+ "name": "蛇屠箱",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "仓库整备·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+10>,生产力<@cc.vup>+10%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 34,
+ "name": "清道夫",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "S.W.E.E.P.",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "拾荒者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+8>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_limit&cost1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 33,
+ "name": "夜烟",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "金属工艺·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 32,
+ "name": "猎蜂",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "近卫专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>近卫>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_guard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "特训记录",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>芯片>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_asc1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 31,
+ "name": "杜宾",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "左膀右臂",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻控制中枢时,控制中枢内所有干员的心情每小时恢复<@cc.vup>+0.05>",
+ "roomType": "中枢",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ctrl_cost",
+ "buffColor": "#005752",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "教官",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,干员的专精技能训练速度<@cc.vup>+25%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_all",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 30,
+ "name": "艾丝黛尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "专注·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>60%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 29,
+ "name": "流星",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 28,
+ "name": "白雪",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "作战指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 27,
+ "name": "末药",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "药理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 26,
+ "name": "深海色",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "专注·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>60%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 25,
+ "name": "远山",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "供应管理",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit6",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "占卜",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>25%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd3",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 24,
+ "name": "斑点",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "能工巧匠",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "金属工艺·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>贵金属>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_gold1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 23,
+ "name": "泡普卡",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "麻烦制造者",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>,仓库容量上限<@cc.vdown>-12>,心情每小时消耗<@cc.vdown>+0.25>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit&cost3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "和谐",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.4>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.2>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one21",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 22,
+ "name": "月见夜",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "交际",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "源石工艺·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 21,
+ "name": "空爆",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "交际",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "爆破材料学·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>80%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 20,
+ "name": "梓兰",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "心理学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+40%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd4",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "供应管理",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit6",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 19,
+ "name": "香草",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 18,
+ "name": "安赛尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "药理学·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>75%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 17,
+ "name": "安德切尔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+20%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "能工巧匠",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>50%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p2",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 16,
+ "name": "史都华德",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "谈判",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单上限<@cc.vup>+5>,心情每小时消耗<@cc.vup>-0.25>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_limit&cost",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "标准化·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 15,
+ "name": "卡缇",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "仓库整备·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+6>,生产力<@cc.vup>+10%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "活泼",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.2>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.4>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one01",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 14,
+ "name": "玫兰莎",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "供应管理",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+25%>,且订单上限<@cc.vup>+1>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit6",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "专注·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>40%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 13,
+ "name": "翎羽",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "先锋专精·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻训练室协助位时,<@cc.kw>先锋>干员的专精技能训练速度<@cc.vup>+30%>",
+ "roomType": "训练室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_train_vanguard1",
+ "buffColor": "#7d0022",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单管理·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 12,
+ "name": "克洛丝",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "慢性子",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站后,生产力首小时<@cc.vup>+15%>,此后每小时<@cc.vup>+2%>,最终达到<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "独处",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vup>+0.7>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 11,
+ "name": "芬",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "急性子",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站后,生产力首小时<@cc.vup>+20%>,此后每小时<@cc.vup>+1%>,最终达到<@cc.vup>+25%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd_add1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 10,
+ "name": "米格鲁",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "仓库整备·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+6>,生产力<@cc.vup>+10%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "活泼",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.2>(同种效果取最高);同时自身心情每小时恢复<@cc.vup>+0.4>",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single&one01",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 9,
+ "name": "炎熔",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "热能充能·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "源石工艺·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>源石>类配方的生产力<@cc.vup>+35%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_originium2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 8,
+ "name": "芙蓉",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "善解人意",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.55>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精1 1级",
+ "skillname": "营养学",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>精英材料>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_evolve1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 7,
+ "name": "巡林者",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "人事管理·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻人力办公室时,人脉资源的联络速度<@cc.vup>+20%>",
+ "roomType": "人力办公室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_hire_spd1",
+ "buffColor": "#565656",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "老当益壮",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>任意类材料>时,副产品的产出概率提升<@cc.vup>60%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_p3",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 6,
+ "name": "夜刀",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "订单分发·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+30%>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd2",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "标准化·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,生产力<@cc.vup>+15%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd1",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 5,
+ "name": "杜林",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "慵懒",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vdown>-0.1>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.2>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one1",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 0,
+ "skill_level": 1,
+ "phase_level": "精0 30级",
+ "skillname": "嗜睡",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,自身心情每小时恢复<@cc.vdown>-0.1>,该宿舍内所有干员的心情每小时恢复<@cc.vup>+0.25>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_all&one2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 4,
+ "name": "黑角",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "仓库整备·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,仓库容量上限<@cc.vup>+10>,生产力<@cc.vup>+10%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_spd&limit3",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "订单管理·α",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻贸易站时,订单获取效率<@cc.vup>+10%>,且订单上限<@cc.vup>+2>",
+ "roomType": "贸易站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_tra_spd&limit1",
+ "buffColor": "#0075a9",
+ "textColor": "#ffffff"
+ }
+ ]
+ },
+ {
+ "key": 3,
+ "name": "12F",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "线索搜集·β",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻会客室时,线索搜集速度提升<@cc.vup>20%>",
+ "roomType": "会客室",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_meet_spd2",
+ "buffColor": "#dd653f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "特训记录",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻加工站加工<@cc.kw>芯片>时,副产品的产出概率提升<@cc.vup>70%>",
+ "roomType": "加工站",
+ "buffCategory": "FUNCTION",
+ "skillIcon": "bskill_ws_asc1",
+ "buffColor": "#e3eb00",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 2,
+ "name": "Castle-3",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "备用能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "作战指导录像",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻制造站时,<@cc.kw>作战记录>类配方的生产力<@cc.vup>+30%>",
+ "roomType": "制造站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_man_exp2",
+ "buffColor": "#ffd800",
+ "textColor": "#333333"
+ }
+ ]
+ },
+ {
+ "key": 1,
+ "name": "Lancet-2",
+ "span": 2,
+ "child_skill": [
+ {
+ "skill_key": 0,
+ "skill_level": 0,
+ "phase_level": "精0 1级",
+ "skillname": "备用能源",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻发电站时,无人机充能速度<@cc.vup>+10%>",
+ "roomType": "发电站",
+ "buffCategory": "OUTPUT",
+ "skillIcon": "bskill_pow_spd1",
+ "buffColor": "#8fc31f",
+ "textColor": "#ffffff"
+ },
+ {
+ "skill_key": 1,
+ "skill_level": 0,
+ "phase_level": "精0 30级",
+ "skillname": "医疗服务",
+ "buffer": false,
+ "buffer_des": [],
+ "des": "进驻宿舍时,使该宿舍内除自身以外心情未满的某个干员每小时恢复<@cc.vup>+0.65>(同种效果取最高)",
+ "roomType": "宿舍",
+ "buffCategory": "RECOVERY",
+ "skillIcon": "bskill_dorm_single2",
+ "buffColor": "#21cdcb",
+ "textColor": "#ffffff"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/ui/src/pages/depot.vue b/ui/src/pages/depot.vue
new file mode 100644
index 000000000..e1b53cd20
--- /dev/null
+++ b/ui/src/pages/depot.vue
@@ -0,0 +1,77 @@
+
+
+
+ 明日方舟工具箱代码 点击复制
+
+
+
+
+
+ 扫描时间:{{ reportData[2] }}
+ 注:万以下的数字并不会计入,如“龙门币 245万” “资质凭证 2万”
+
+ {{ categoryName.slice(1) }}
+
+
+
+
+
+
+ {{ itemData['key'] }}
+ 拥有:{{ itemData['number'] }}
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/readme.vue b/ui/src/pages/readme.vue
new file mode 100644
index 000000000..11cc14284
--- /dev/null
+++ b/ui/src/pages/readme.vue
@@ -0,0 +1,164 @@
+
+
+
+
资源合集
+
+
Mower
+
+ 介绍视频(过时):BV1KT411s7Ar
+
+
+ 排班表编写教程(过时):BV1UM4y1y7bA
+
+
自定义排班教程(过时):
+
+ -
+ 红松(BV1oC4y1U7bo)
+
+ -
+ 跃跃(BV1Bz4y1w7mj)
+
+ -
+ 自动化(BV1Uw411N7WJ)
+
+
+
+ 在线文档:https://arkmowers.github.io/arknights-mower/
+
+
+ 项目源码:https://github.com/ArkMowers/arknights-mower
+
+
+ Mower 教程(施工中):https://blog.zhaozuohong.vip/categories/arknights-mower/
+
+
+ 从源码运行 mower:https://blog.zhaozuohong.vip/2023/08/02/run-arknights-mower-from-source/
+
+
安装与更新:
+
+ -
+ (推荐)群文件内获取新版更新器(更新器源码:https://github.com/Funny-ppt/MowerUpdater)
+
+ -
+ (备用)群文件内获取旧版更新器(更新器源码:https://github.com/ArkMowers/updater)
+
+ -
+ (不推荐)从 GitHub Actions 下载 CI 最新构建产物:https://github.com/ArkMowers/arknights-mower/actions
+
+ -
+ (备用)从 GitHub Release 下载:https://github.com/ArkMowers/arknights-mower/releases
+
+ -
+ (备用)直接从
+ https://mower.zhaozuohong.vip
+ 下载
+
+
+
+
+
Mower0
+
+ 介绍视频:BV1qu4y1k7AX
+
+
+ 项目源码:https://github.com/Bidgecfah/Mower0
+
+
从群文件下载
+
+
+
+
其它资源
+
+ 罗德岛物价局:https://github.com/Bidgecfah/Rhodes-Island-Bureau-of-Price
+
+
基建模拟器:
+
+ -
+ 在线使用(https://infrastsim.zhaozuohong.vip)
+
+ -
+ 项目源码(https://github.com/Funny-ppt/InfrastSim)
+
+ -
+ 前端源码(https://github.com/Funny-ppt/InfrastSimUI)
+
+
+
+ 明日方舟工具箱(基建技能、养成规划):https://arkn.lolicon.app
+
+
Maa:https://maa.plus/
+
+ 明日方舟定价理论(施工中):https://bidgecfah.gitee.io/2023/07/12/%E6%98%8E%E6%97%A5%E6%96%B9%E8%88%9F%E5%AE%9A%E4%BB%B7%E7%90%86%E8%AE%BA/
+
+
+
+
+
+
diff --git a/ui/src/pages/record.vue b/ui/src/pages/record.vue
new file mode 100644
index 000000000..b5e1f61de
--- /dev/null
+++ b/ui/src/pages/record.vue
@@ -0,0 +1,173 @@
+
+
+
干员基建报表
+
+
+
+
+
+
+
+
{{ groupData.groupName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/report.vue b/ui/src/pages/report.vue
new file mode 100644
index 000000000..e451b8237
--- /dev/null
+++ b/ui/src/pages/report.vue
@@ -0,0 +1,484 @@
+
+
+
+ 基建报表
+
+ 若没有数据显示,请查看tmp文件夹中的report.csv文件
+ 若存在空数据删掉对应行或自行填补一个数据
+ report.csv不存在建议先让mower看看你的基报
+
+
+
+
+ 若没有数据显示,请查看tmp文件夹中的report.csv文件
+ 若存在空数据删掉对应行或自行填补一个数据
+ report.csv不存在建议先让mower看看你的基报
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/pages/stage_data/event_data.json b/ui/src/pages/stage_data/event_data.json
new file mode 100644
index 000000000..0d53287c2
--- /dev/null
+++ b/ui/src/pages/stage_data/event_data.json
@@ -0,0 +1,413 @@
+[
+ {
+ "id": "",
+ "name": "上次作战",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 0
+ },
+ {
+ "id": "1-7",
+ "name": "1-7",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 1
+ },
+ {
+ "id": "Annihilation",
+ "name": "剿灭",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 2
+ },
+ {
+ "id": "LS-6",
+ "name": "经验书",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 3
+ },
+ {
+ "id": "CE-6",
+ "name": "龙门币",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ "key": 4
+ },
+ {
+ "id": "AP-5",
+ "name": "红票",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ "key": 5
+ },
+ {
+ "id": "SK-6",
+ "name": "碳条",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0,
+ "key": 6
+ },
+ {
+ "id": "CA-5",
+ "name": "技能书",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1,
+ "key": 7
+ },
+ {
+ "id": "PR-A-2",
+ "name": "重装医疗2",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 0,
+ "周三": 0,
+ "周四": 1,
+ "周五": 1,
+ "周六": 0,
+ "周日": 1,
+ "key": 8
+ },
+ {
+ "id": "PR-B-2",
+ "name": "狙击术士2",
+ "drop": "",
+ "end": -1,
+ "周一": 1,
+ "周二": 1,
+ "周三": 0,
+ "周四": 0,
+ "周五": 1,
+ "周六": 1,
+ "周日": 0,
+ "key": 9
+ },
+ {
+ "id": "PR-C-2",
+ "name": "先锋辅助2",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 0,
+ "周三": 1,
+ "周四": 1,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ "key": 10
+ },
+ {
+ "id": "PR-D-2",
+ "name": "近卫特种2",
+ "drop": "",
+ "end": -1,
+ "周一": 0,
+ "周二": 1,
+ "周三": 1,
+ "周四": 0,
+ "周五": 0,
+ "周六": 1,
+ "周日": 1,
+ "key": 11
+ },
+ {
+ "id": "DT-1",
+ "name": "源石虫农家乐",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "酯原料",
+ "破损装置",
+ "异铁碎片",
+ "双酮",
+ "代糖",
+ "源岩"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 12
+ },
+ {
+ "id": "DT-2",
+ "name": "雨林解毒炖汤",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "酯原料",
+ "破损装置",
+ "异铁碎片",
+ "双酮",
+ "代糖",
+ "源岩"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 13
+ },
+ {
+ "id": "DT-3",
+ "name": "黄金萝卜",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "聚酸酯",
+ "固源岩"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 14
+ },
+ {
+ "id": "DT-4",
+ "name": "烤串与预言",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "异铁",
+ "糖"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 15
+ },
+ {
+ "id": "DT-5",
+ "name": "会饮图",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "酮凝集",
+ "装置"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 16
+ },
+ {
+ "id": "DT-6",
+ "name": "海鲜拼盘",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "聚酸酯组"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 17
+ },
+ {
+ "id": "DT-7",
+ "name": "健康零食",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "炽合金"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 18
+ },
+ {
+ "id": "DT-8",
+ "name": "罗德岛大食堂",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [
+ "今日食谱",
+ "褐素纤维"
+ ],
+ "首次掉落": [
+ "今日食谱",
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 19
+ },
+ {
+ "id": "DT-TR-1",
+ "name": "美食指南",
+ "drop": {
+ "突袭首次掉落": [],
+ "常规掉落": [],
+ "首次掉落": [
+ "至纯源石"
+ ],
+ "特殊掉落": [],
+ "额外物资": []
+ },
+ "end": 1726430399,
+ "周一": 1,
+ "周二": 1,
+ "周三": 1,
+ "周四": 1,
+ "周五": 1,
+ "周六": 1,
+ "周日": 1,
+ "key": 20
+ }
+]
\ No newline at end of file
diff --git a/ui/src/pages/theme/chalk.json b/ui/src/pages/theme/chalk.json
new file mode 100644
index 000000000..013b890ed
--- /dev/null
+++ b/ui/src/pages/theme/chalk.json
@@ -0,0 +1,363 @@
+{
+ "color": ["#fc97af", "#87f7cf", "#f7f494", "#72ccff", "#f7c5a0", "#d4a4eb", "#d2f5a6", "#76f2f2"],
+ "backgroundColor": "rgba(41,52,65,1)",
+ "textStyle": {},
+ "title": {
+ "textStyle": {
+ "color": "#ffffff"
+ },
+ "subtextStyle": {
+ "color": "#dddddd"
+ }
+ },
+ "line": {
+ "itemStyle": {
+ "borderWidth": "4"
+ },
+ "lineStyle": {
+ "width": "3"
+ },
+ "symbolSize": "0",
+ "symbol": "circle",
+ "smooth": true
+ },
+ "radar": {
+ "itemStyle": {
+ "borderWidth": "4"
+ },
+ "lineStyle": {
+ "width": "3"
+ },
+ "symbolSize": "0",
+ "symbol": "circle",
+ "smooth": true
+ },
+ "bar": {
+ "itemStyle": {
+ "barBorderWidth": 0,
+ "barBorderColor": "#ccc"
+ }
+ },
+ "pie": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "scatter": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "boxplot": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "parallel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "sankey": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "funnel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "gauge": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "candlestick": {
+ "itemStyle": {
+ "color": "#fc97af",
+ "color0": "transparent",
+ "borderColor": "#fc97af",
+ "borderColor0": "#87f7cf",
+ "borderWidth": "2"
+ }
+ },
+ "graph": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ },
+ "lineStyle": {
+ "width": "1",
+ "color": "#ffffff"
+ },
+ "symbolSize": "0",
+ "symbol": "circle",
+ "smooth": true,
+ "color": [
+ "#fc97af",
+ "#87f7cf",
+ "#f7f494",
+ "#72ccff",
+ "#f7c5a0",
+ "#d4a4eb",
+ "#d2f5a6",
+ "#76f2f2"
+ ],
+ "label": {
+ "color": "#293441"
+ }
+ },
+ "map": {
+ "itemStyle": {
+ "areaColor": "#f3f3f3",
+ "borderColor": "#999999",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#893448"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,178,72,1)",
+ "borderColor": "#eb8146",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(137,52,72)"
+ }
+ }
+ },
+ "geo": {
+ "itemStyle": {
+ "areaColor": "#f3f3f3",
+ "borderColor": "#999999",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#893448"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,178,72,1)",
+ "borderColor": "#eb8146",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(137,52,72)"
+ }
+ }
+ },
+ "categoryAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#666666"
+ }
+ },
+ "axisTick": {
+ "show": false,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#aaaaaa"
+ },
+ "splitLine": {
+ "show": false,
+ "lineStyle": {
+ "color": ["#e6e6e6"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.05)", "rgba(200,200,200,0.02)"]
+ }
+ }
+ },
+ "valueAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#666666"
+ }
+ },
+ "axisTick": {
+ "show": false,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#aaaaaa"
+ },
+ "splitLine": {
+ "show": false,
+ "lineStyle": {
+ "color": ["#e6e6e6"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.05)", "rgba(200,200,200,0.02)"]
+ }
+ }
+ },
+ "logAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#666666"
+ }
+ },
+ "axisTick": {
+ "show": false,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#aaaaaa"
+ },
+ "splitLine": {
+ "show": false,
+ "lineStyle": {
+ "color": ["#e6e6e6"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.05)", "rgba(200,200,200,0.02)"]
+ }
+ }
+ },
+ "timeAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#666666"
+ }
+ },
+ "axisTick": {
+ "show": false,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#aaaaaa"
+ },
+ "splitLine": {
+ "show": false,
+ "lineStyle": {
+ "color": ["#e6e6e6"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.05)", "rgba(200,200,200,0.02)"]
+ }
+ }
+ },
+ "toolbox": {
+ "iconStyle": {
+ "borderColor": "#999999"
+ },
+ "emphasis": {
+ "iconStyle": {
+ "borderColor": "#666666"
+ }
+ }
+ },
+ "legend": {
+ "textStyle": {
+ "color": "#999999"
+ }
+ },
+ "tooltip": {
+ "axisPointer": {
+ "lineStyle": {
+ "color": "#cccccc",
+ "width": 1
+ },
+ "crossStyle": {
+ "color": "#cccccc",
+ "width": 1
+ }
+ }
+ },
+ "timeline": {
+ "lineStyle": {
+ "color": "#87f7cf",
+ "width": 1
+ },
+ "itemStyle": {
+ "color": "#87f7cf",
+ "borderWidth": 1
+ },
+ "controlStyle": {
+ "color": "#87f7cf",
+ "borderColor": "#87f7cf",
+ "borderWidth": 0.5
+ },
+ "checkpointStyle": {
+ "color": "#fc97af",
+ "borderColor": "#fc97af"
+ },
+ "label": {
+ "color": "#87f7cf"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "color": "#f7f494"
+ },
+ "controlStyle": {
+ "color": "#87f7cf",
+ "borderColor": "#87f7cf",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#87f7cf"
+ }
+ }
+ },
+ "visualMap": {
+ "color": ["#fc97af", "#87f7cf"]
+ },
+ "dataZoom": {
+ "backgroundColor": "rgba(255,255,255,0)",
+ "dataBackgroundColor": "rgba(114,204,255,1)",
+ "fillerColor": "rgba(114,204,255,0.2)",
+ "handleColor": "#72ccff",
+ "handleSize": "100%",
+ "textStyle": {
+ "color": "#333333"
+ }
+ },
+ "markPoint": {
+ "label": {
+ "color": "#293441"
+ },
+ "emphasis": {
+ "label": {
+ "color": "#293441"
+ }
+ }
+ }
+}
diff --git a/ui/src/pages/theme/dark.json b/ui/src/pages/theme/dark.json
new file mode 100644
index 000000000..ebb6d41c6
--- /dev/null
+++ b/ui/src/pages/theme/dark.json
@@ -0,0 +1,378 @@
+{
+ "color": [
+ "#dd6b66",
+ "#759aa0",
+ "#e69d87",
+ "#8dc1a9",
+ "#ea7e53",
+ "#eedd78",
+ "#73a373",
+ "#73b9bc",
+ "#7289ab",
+ "#91ca8c",
+ "#f49f42"
+ ],
+ "backgroundColor": "rgba(51,51,51,1)",
+ "textStyle": {},
+ "title": {
+ "textStyle": {
+ "color": "#eeeeee"
+ },
+ "subtextStyle": {
+ "color": "#aaaaaa"
+ }
+ },
+ "line": {
+ "itemStyle": {
+ "borderWidth": 1
+ },
+ "lineStyle": {
+ "width": 2
+ },
+ "symbolSize": 4,
+ "symbol": "circle",
+ "smooth": false
+ },
+ "radar": {
+ "itemStyle": {
+ "borderWidth": 1
+ },
+ "lineStyle": {
+ "width": 2
+ },
+ "symbolSize": 4,
+ "symbol": "circle",
+ "smooth": false
+ },
+ "bar": {
+ "itemStyle": {
+ "barBorderWidth": 0,
+ "barBorderColor": "#ccc"
+ }
+ },
+ "pie": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "scatter": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "boxplot": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "parallel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "sankey": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "funnel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "gauge": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "candlestick": {
+ "itemStyle": {
+ "color": "#fd1050",
+ "color0": "#0cf49b",
+ "borderColor": "#fd1050",
+ "borderColor0": "#0cf49b",
+ "borderWidth": 1
+ }
+ },
+ "graph": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ },
+ "lineStyle": {
+ "width": 1,
+ "color": "#aaaaaa"
+ },
+ "symbolSize": 4,
+ "symbol": "circle",
+ "smooth": false,
+ "color": [
+ "#dd6b66",
+ "#759aa0",
+ "#e69d87",
+ "#8dc1a9",
+ "#ea7e53",
+ "#eedd78",
+ "#73a373",
+ "#73b9bc",
+ "#7289ab",
+ "#91ca8c",
+ "#f49f42"
+ ],
+ "label": {
+ "color": "#eeeeee"
+ }
+ },
+ "map": {
+ "itemStyle": {
+ "areaColor": "#eee",
+ "borderColor": "#444",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#000"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,215,0,0.8)",
+ "borderColor": "#444",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(100,0,0)"
+ }
+ }
+ },
+ "geo": {
+ "itemStyle": {
+ "areaColor": "#eee",
+ "borderColor": "#444",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#000"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,215,0,0.8)",
+ "borderColor": "#444",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(100,0,0)"
+ }
+ }
+ },
+ "categoryAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#eeeeee"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#aaaaaa"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["#eeeeee"]
+ }
+ }
+ },
+ "valueAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#eeeeee"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#aaaaaa"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["#eeeeee"]
+ }
+ }
+ },
+ "logAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#eeeeee"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#aaaaaa"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["#eeeeee"]
+ }
+ }
+ },
+ "timeAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#eeeeee"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#aaaaaa"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["#eeeeee"]
+ }
+ }
+ },
+ "toolbox": {
+ "iconStyle": {
+ "borderColor": "#999999"
+ },
+ "emphasis": {
+ "iconStyle": {
+ "borderColor": "#666666"
+ }
+ }
+ },
+ "legend": {
+ "textStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "tooltip": {
+ "axisPointer": {
+ "lineStyle": {
+ "color": "#eeeeee",
+ "width": "1"
+ },
+ "crossStyle": {
+ "color": "#eeeeee",
+ "width": "1"
+ }
+ }
+ },
+ "timeline": {
+ "lineStyle": {
+ "color": "#eeeeee",
+ "width": 1
+ },
+ "itemStyle": {
+ "color": "#dd6b66",
+ "borderWidth": 1
+ },
+ "controlStyle": {
+ "color": "#eeeeee",
+ "borderColor": "#eeeeee",
+ "borderWidth": 0.5
+ },
+ "checkpointStyle": {
+ "color": "#e43c59",
+ "borderColor": "#c23531"
+ },
+ "label": {
+ "color": "#eeeeee"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "color": "#a9334c"
+ },
+ "controlStyle": {
+ "color": "#eeeeee",
+ "borderColor": "#eeeeee",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#eeeeee"
+ }
+ }
+ },
+ "visualMap": {
+ "color": ["#bf444c", "#d88273", "#f6efa6"]
+ },
+ "dataZoom": {
+ "backgroundColor": "rgba(47,69,84,0)",
+ "dataBackgroundColor": "rgba(255,255,255,0.3)",
+ "fillerColor": "rgba(167,183,204,0.4)",
+ "handleColor": "#a7b7cc",
+ "handleSize": "100%",
+ "textStyle": {
+ "color": "#eeeeee"
+ }
+ },
+ "markPoint": {
+ "label": {
+ "color": "#eeeeee"
+ },
+ "emphasis": {
+ "label": {
+ "color": "#eeeeee"
+ }
+ }
+ }
+}
diff --git a/ui/src/pages/theme/roma.json b/ui/src/pages/theme/roma.json
new file mode 100644
index 000000000..2db2a7be6
--- /dev/null
+++ b/ui/src/pages/theme/roma.json
@@ -0,0 +1,396 @@
+{
+ "color": [
+ "#e01f54",
+ "#001852",
+ "#f5e8c8",
+ "#b8d2c7",
+ "#c6b38e",
+ "#a4d8c2",
+ "#f3d999",
+ "#d3758f",
+ "#dcc392",
+ "#2e4783",
+ "#82b6e9",
+ "#ff6347",
+ "#a092f1",
+ "#0a915d",
+ "#eaf889",
+ "#6699FF",
+ "#ff6666",
+ "#3cb371",
+ "#d5b158",
+ "#38b6b6"
+ ],
+ "backgroundColor": "rgba(0,0,0,0)",
+ "textStyle": {},
+ "title": {
+ "textStyle": {
+ "color": "#333333"
+ },
+ "subtextStyle": {
+ "color": "#aaaaaa"
+ }
+ },
+ "line": {
+ "itemStyle": {
+ "borderWidth": 1
+ },
+ "lineStyle": {
+ "width": 2
+ },
+ "symbolSize": 4,
+ "symbol": "emptyCircle",
+ "smooth": false
+ },
+ "radar": {
+ "itemStyle": {
+ "borderWidth": 1
+ },
+ "lineStyle": {
+ "width": 2
+ },
+ "symbolSize": 4,
+ "symbol": "emptyCircle",
+ "smooth": false
+ },
+ "bar": {
+ "itemStyle": {
+ "barBorderWidth": 0,
+ "barBorderColor": "#ccc"
+ }
+ },
+ "pie": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "scatter": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "boxplot": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "parallel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "sankey": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "funnel": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "gauge": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ }
+ },
+ "candlestick": {
+ "itemStyle": {
+ "color": "#e01f54",
+ "color0": "#001852",
+ "borderColor": "#f5e8c8",
+ "borderColor0": "#b8d2c7",
+ "borderWidth": 1
+ }
+ },
+ "graph": {
+ "itemStyle": {
+ "borderWidth": 0,
+ "borderColor": "#ccc"
+ },
+ "lineStyle": {
+ "width": 1,
+ "color": "#aaa"
+ },
+ "symbolSize": 4,
+ "symbol": "emptyCircle",
+ "smooth": false,
+ "color": [
+ "#e01f54",
+ "#001852",
+ "#f5e8c8",
+ "#b8d2c7",
+ "#c6b38e",
+ "#a4d8c2",
+ "#f3d999",
+ "#d3758f",
+ "#dcc392",
+ "#2e4783",
+ "#82b6e9",
+ "#ff6347",
+ "#a092f1",
+ "#0a915d",
+ "#eaf889",
+ "#6699FF",
+ "#ff6666",
+ "#3cb371",
+ "#d5b158",
+ "#38b6b6"
+ ],
+ "label": {
+ "color": "#eee"
+ }
+ },
+ "map": {
+ "itemStyle": {
+ "areaColor": "#eeeeee",
+ "borderColor": "#444444",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#000000"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,215,0,0.8)",
+ "borderColor": "#444",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(100,0,0)"
+ }
+ }
+ },
+ "geo": {
+ "itemStyle": {
+ "areaColor": "#eeeeee",
+ "borderColor": "#444444",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#000000"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "areaColor": "rgba(255,215,0,0.8)",
+ "borderColor": "#444",
+ "borderWidth": 1
+ },
+ "label": {
+ "color": "rgb(100,0,0)"
+ }
+ }
+ },
+ "categoryAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#333"
+ },
+ "splitLine": {
+ "show": false,
+ "lineStyle": {
+ "color": ["#ccc"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
+ }
+ }
+ },
+ "valueAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#333"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#ccc"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
+ }
+ }
+ },
+ "logAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#333"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#ccc"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
+ }
+ }
+ },
+ "timeAxis": {
+ "axisLine": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisTick": {
+ "show": true,
+ "lineStyle": {
+ "color": "#333"
+ }
+ },
+ "axisLabel": {
+ "show": true,
+ "color": "#333"
+ },
+ "splitLine": {
+ "show": true,
+ "lineStyle": {
+ "color": ["#ccc"]
+ }
+ },
+ "splitArea": {
+ "show": false,
+ "areaStyle": {
+ "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
+ }
+ }
+ },
+ "toolbox": {
+ "iconStyle": {
+ "borderColor": "#999999"
+ },
+ "emphasis": {
+ "iconStyle": {
+ "borderColor": "#666666"
+ }
+ }
+ },
+ "legend": {
+ "textStyle": {
+ "color": "#333333"
+ }
+ },
+ "tooltip": {
+ "axisPointer": {
+ "lineStyle": {
+ "color": "#cccccc",
+ "width": 1
+ },
+ "crossStyle": {
+ "color": "#cccccc",
+ "width": 1
+ }
+ }
+ },
+ "timeline": {
+ "lineStyle": {
+ "color": "#293c55",
+ "width": 1
+ },
+ "itemStyle": {
+ "color": "#293c55",
+ "borderWidth": 1
+ },
+ "controlStyle": {
+ "color": "#293c55",
+ "borderColor": "#293c55",
+ "borderWidth": 0.5
+ },
+ "checkpointStyle": {
+ "color": "#e43c59",
+ "borderColor": "#c23531"
+ },
+ "label": {
+ "color": "#293c55"
+ },
+ "emphasis": {
+ "itemStyle": {
+ "color": "#a9334c"
+ },
+ "controlStyle": {
+ "color": "#293c55",
+ "borderColor": "#293c55",
+ "borderWidth": 0.5
+ },
+ "label": {
+ "color": "#293c55"
+ }
+ }
+ },
+ "visualMap": {
+ "color": ["#e01f54", "#e7dbc3"]
+ },
+ "dataZoom": {
+ "backgroundColor": "rgba(47,69,84,0)",
+ "dataBackgroundColor": "rgba(47,69,84,0.3)",
+ "fillerColor": "rgba(167,183,204,0.4)",
+ "handleColor": "#a7b7cc",
+ "handleSize": "100%",
+ "textStyle": {
+ "color": "#333333"
+ }
+ },
+ "markPoint": {
+ "label": {
+ "color": "#eee"
+ },
+ "emphasis": {
+ "label": {
+ "color": "#eee"
+ }
+ }
+ }
+}
diff --git a/ui/src/router.js b/ui/src/router.js
new file mode 100644
index 000000000..e53772178
--- /dev/null
+++ b/ui/src/router.js
@@ -0,0 +1,18 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import { routes } from '@/routes'
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes
+})
+
+router.beforeEach((to, from) => {
+ if (from.query.token && !to.query.token) {
+ return {
+ path: to.path,
+ query: from.query
+ }
+ }
+})
+
+export default router
diff --git a/ui/src/routes.js b/ui/src/routes.js
new file mode 100644
index 000000000..cfcb1ce42
--- /dev/null
+++ b/ui/src/routes.js
@@ -0,0 +1,79 @@
+export const routes = [
+ {
+ path: '/',
+ children: [
+ {
+ path: 'readme',
+ component: () => import('@/pages/readme.vue'),
+ meta: { title: '帮助' },
+ name: 'readme'
+ },
+ {
+ path: '',
+ component: () => import('@/pages/Log.vue'),
+ meta: { title: '日志' },
+ name: 'log'
+ },
+ {
+ path: 'plan-editor',
+ component: () => import('@/pages/Plan.vue'),
+ meta: { title: '排班' },
+ name: 'plan'
+ },
+ {
+ path: 'settings',
+ component: () => import('@/pages/Settings.vue'),
+ meta: { title: '设置' },
+ name: 'settings'
+ },
+ {
+ path: 'aio',
+ component: () => import('@/pages/Material_all_in_one.vue'),
+ meta: { title: '设置' },
+ name: 'aio'
+ },
+ {
+ path: 'doc',
+ component: () => import('@/pages/Doc.vue'),
+ meta: { title: '文档' },
+ name: 'doc'
+ },
+ {
+ path: 'BasementSkill',
+ component: () => import('@/pages/BasementSkill.vue'),
+ meta: { title: '基建技能' },
+ name: 'BasementSkill'
+ },
+ {
+ path: 'record',
+ children: [
+ {
+ path: 'line',
+ component: () => import('@/pages/RecordLine.vue'),
+ meta: { title: '心情曲线' },
+ name: 'record_line'
+ },
+ {
+ path: 'depot',
+ component: () => import('@/pages/depot.vue'),
+ meta: { title: '仓库数据' },
+ name: 'depot'
+ },
+ {
+ path: 'pie',
+ component: () => import('@/pages/RecordPie.vue'),
+ meta: { title: '心情饼图' },
+ name: 'record_pie'
+ },
+ {
+ path: 'report',
+ component: () => import('@/pages/report.vue'),
+ meta: { title: '基建报告' },
+ name: 'report'
+ }
+ ]
+ }
+ ]
+ },
+ { path: '/:pathMatch(.*)', component: () => import('@/pages/NotFound.vue') }
+]
diff --git a/ui/src/stores/config.js b/ui/src/stores/config.js
new file mode 100644
index 000000000..253bb48f5
--- /dev/null
+++ b/ui/src/stores/config.js
@@ -0,0 +1,350 @@
+import axios from 'axios'
+import { defineStore } from 'pinia'
+import { inject, ref, watchEffect } from 'vue'
+
+export const useConfigStore = defineStore('config', () => {
+ const adb = ref('')
+ const drone_count_limit = ref(0)
+ const drone_room = ref('')
+ const drone_interval = ref(4)
+ const enable_party = ref(true)
+ const leifeng_mode = ref(true)
+ const free_blacklist = ref([])
+ const maa_adb_path = ref('')
+ const maa_enable = ref(false)
+ const maa_path = ref('')
+ const maa_expiring_medicine = ref(true)
+ const maa_weekly_plan = ref([])
+ const maa_weekly_plan1 = ref([])
+ const maa_rg_enable = ref(0)
+ const maa_long_task_type = ref('rogue')
+ const mail_enable = ref(false)
+ const account = ref('')
+ const pass_code = ref('')
+ const recipient = ref('')
+ const custom_smtp_server = ref({})
+ const package_type = ref('official')
+ const reload_room = ref('')
+ const run_order_delay = ref(10)
+ const start_automatically = ref(false)
+ const maa_mall_buy = ref('')
+ const maa_mall_blacklist = ref('')
+ const shop_list = ref([])
+ const maa_gap = ref(false)
+ const simulator = ref({ name: '', index: -1 })
+ const resting_threshold = ref(50)
+ const theme = ref('light')
+ const tap_to_launch_game = ref(false)
+ const exit_game_when_idle = ref(true)
+ const close_simulator_when_idle = ref(false)
+ const maa_conn_preset = ref('General')
+ const maa_touch_option = ref('maatouch')
+ const maa_mall_ignore_blacklist_when_full = ref(false)
+ const maa_rg_sleep_min = ref('00:00')
+ const maa_rg_sleep_max = ref('00:00')
+ const maa_credit_fight = ref(true)
+ const maa_depot_enable = ref(false)
+ const maa_rg_theme = ref('Mizuki')
+ const rogue = ref({})
+ const sss = ref({})
+ const screenshot = ref(0)
+ const screenshot_interval = ref(500)
+ const mail_subject = ref('')
+ const skland_enable = ref(false)
+ const skland_info = ref([])
+ const recruit_enable = ref(true)
+ const recruitment_permit = ref(30)
+ const recruit_robot = ref(true)
+ const recruit_auto_only5 = ref(true)
+ const run_order_grandet_mode = ref({})
+ const check_mail_enable = ref(true)
+ const report_enable = ref(true)
+ const recruit_gap = ref(false)
+ const recruit_auto_5 = ref('hand')
+ const webview = ref({ scale: 1.0 })
+ const shop_collect_enable = ref(true)
+ const meeting_level = ref(3)
+ const fix_mumu12_adb_disconnect = ref(false)
+ const ra_timeout = ref(30)
+ const sf_target = ref('结局A')
+ const touch_method = ref('scrcpy')
+ const free_room = ref(false)
+ const sign_in = ref({ enable: true })
+ const droidcast = ref({})
+ const visit_friend = ref(true)
+ const credit_fight = ref({})
+ const custom_screenshot = ref({})
+ const check_for_updates = ref(true)
+ const notification_level = ref('INFO')
+ const waiting_scene = ref({})
+ const exipring_medicine_on_weekend = ref(false)
+
+ async function load_shop() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/shop`)
+ const mall_list = []
+ for (const i of response.data) {
+ mall_list.push({
+ value: i,
+ label: i
+ })
+ }
+ shop_list.value = mall_list
+ }
+
+ async function load_config() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/conf`)
+ adb.value = response.data.adb
+ drone_count_limit.value = response.data.drone_count_limit
+ drone_room.value = response.data.drone_room
+ drone_interval.value = response.data.drone_interval
+ enable_party.value = response.data.enable_party != 0
+ leifeng_mode.value = response.data.leifeng_mode != 0
+ free_blacklist.value =
+ response.data.free_blacklist == '' ? [] : response.data.free_blacklist.split(',')
+ maa_adb_path.value = response.data.maa_adb_path
+ maa_enable.value = response.data.maa_enable != 0
+ maa_path.value = response.data.maa_path
+ maa_rg_enable.value = response.data.maa_rg_enable == 1
+ maa_long_task_type.value = response.data.maa_long_task_type
+ maa_expiring_medicine.value = response.data.maa_expiring_medicine
+ maa_weekly_plan.value = response.data.maa_weekly_plan
+ maa_weekly_plan1.value = response.data.maa_weekly_plan1
+ mail_enable.value = response.data.mail_enable != 0
+ account.value = response.data.account
+ pass_code.value = response.data.pass_code
+ recipient.value = response.data.recipient
+ custom_smtp_server.value = response.data.custom_smtp_server
+ package_type.value = response.data.package_type == 1 ? 'official' : 'bilibili'
+ reload_room.value = response.data.reload_room == '' ? [] : response.data.reload_room.split(',')
+ run_order_delay.value = response.data.run_order_delay
+ start_automatically.value = response.data.start_automatically
+ maa_mall_buy.value =
+ response.data.maa_mall_buy == '' ? [] : response.data.maa_mall_buy.split(',')
+ maa_mall_blacklist.value =
+ response.data.maa_mall_blacklist == '' ? [] : response.data.maa_mall_blacklist.split(',')
+ maa_gap.value = response.data.maa_gap
+ simulator.value = response.data.simulator
+ resting_threshold.value = response.data.resting_threshold * 100
+ theme.value = response.data.theme
+ tap_to_launch_game.value = response.data.tap_to_launch_game
+ tap_to_launch_game.value.enable = tap_to_launch_game.value.enable ? 'tap' : 'adb'
+ exit_game_when_idle.value = response.data.exit_game_when_idle
+ close_simulator_when_idle.value = response.data.close_simulator_when_idle
+ maa_conn_preset.value = response.data.maa_conn_preset
+ maa_touch_option.value = response.data.maa_touch_option
+ maa_mall_ignore_blacklist_when_full.value = response.data.maa_mall_ignore_blacklist_when_full
+ maa_rg_sleep_max.value = response.data.maa_rg_sleep_max
+ maa_rg_sleep_min.value = response.data.maa_rg_sleep_min
+ maa_credit_fight.value = response.data.maa_credit_fight
+ maa_depot_enable.value = response.data.maa_depot_enable
+ maa_rg_theme.value = response.data.maa_rg_theme
+ rogue.value = response.data.rogue
+ sss.value = response.data.sss
+ screenshot.value = response.data.screenshot
+ screenshot_interval.value = response.data.screenshot_interval
+ mail_subject.value = response.data.mail_subject
+ skland_enable.value = response.data.skland_enable != 0
+ skland_info.value = response.data.skland_info
+ recruit_enable.value = response.data.recruit_enable
+ recruitment_permit.value = response.data.recruitment_permit
+ recruit_robot.value = response.data.recruit_robot
+ recruit_auto_only5.value = response.data.recruit_auto_only5
+ run_order_grandet_mode.value = response.data.run_order_grandet_mode
+ check_mail_enable.value = response.data.check_mail_enable
+ report_enable.value = response.data.report_enable
+ recruit_gap.value = response.data.recruit_gap
+ recruit_auto_5.value = response.data.recruit_auto_5
+ webview.value = response.data.webview
+ shop_collect_enable.value = response.data.shop_collect_enable
+ meeting_level.value = response.data.meeting_level
+ fix_mumu12_adb_disconnect.value = response.data.fix_mumu12_adb_disconnect
+ ra_timeout.value = response.data.reclamation_algorithm.timeout
+ sf_target.value = response.data.secret_front.target
+ touch_method.value = response.data.touch_method
+ free_room.value = response.data.free_room
+ sign_in.value = response.data.sign_in
+ droidcast.value = response.data.droidcast
+ visit_friend.value = response.data.visit_friend
+ credit_fight.value = response.data.credit_fight
+ custom_screenshot.value = response.data.custom_screenshot
+ check_for_updates.value = response.data.check_for_updates
+ notification_level.value = response.data.notification_level
+ waiting_scene.value = response.data.waiting_scene
+ exipring_medicine_on_weekend.value = response.data.exipring_medicine_on_weekend
+ }
+
+ function build_config() {
+ return {
+ account: account.value,
+ adb: adb.value,
+ drone_count_limit: drone_count_limit.value,
+ drone_room: drone_room.value,
+ drone_interval: drone_interval.value,
+ enable_party: enable_party.value ? 1 : 0,
+ leifeng_mode: leifeng_mode.value ? 1 : 0,
+ free_blacklist: free_blacklist.value.join(','),
+ maa_adb_path: maa_adb_path.value,
+ maa_enable: maa_enable.value ? 1 : 0,
+ maa_path: maa_path.value,
+ maa_rg_enable: maa_rg_enable.value ? 1 : 0,
+ maa_long_task_type: maa_long_task_type.value,
+ maa_expiring_medicine: maa_expiring_medicine.value,
+ maa_weekly_plan: maa_weekly_plan.value,
+ maa_weekly_plan1: maa_weekly_plan1.value,
+ mail_enable: mail_enable.value ? 1 : 0,
+ package_type: package_type.value == 'official' ? 1 : 0,
+ pass_code: pass_code.value,
+ recipient: recipient.value,
+ custom_smtp_server: custom_smtp_server.value,
+ reload_room: reload_room.value.join(','),
+ run_order_delay: run_order_delay.value,
+ start_automatically: start_automatically.value,
+ maa_mall_buy: maa_mall_buy.value.join(','),
+ maa_mall_blacklist: maa_mall_blacklist.value.join(','),
+ maa_gap: maa_gap.value,
+ simulator: simulator.value,
+ theme: theme.value,
+ resting_threshold: resting_threshold.value / 100,
+ tap_to_launch_game: {
+ enable: tap_to_launch_game.value.enable == 'tap',
+ x: tap_to_launch_game.value.x,
+ y: tap_to_launch_game.value.y
+ },
+ exit_game_when_idle: exit_game_when_idle.value,
+ close_simulator_when_idle: close_simulator_when_idle.value,
+ maa_conn_preset: maa_conn_preset.value,
+ maa_touch_option: maa_touch_option.value,
+ maa_mall_ignore_blacklist_when_full: maa_mall_ignore_blacklist_when_full.value,
+ maa_rg_sleep_max: maa_rg_sleep_max.value,
+ maa_rg_sleep_min: maa_rg_sleep_min.value,
+ maa_credit_fight: maa_credit_fight.value,
+ maa_depot_enable: maa_depot_enable.value,
+ maa_rg_theme: maa_rg_theme.value,
+ rogue: rogue.value,
+ sss: sss.value,
+ screenshot: screenshot.value,
+ screenshot_interval: screenshot_interval.value,
+ mail_subject: mail_subject.value,
+ skland_enable: skland_enable.value,
+ skland_info: skland_info.value,
+ recruit_enable: recruit_enable.value,
+ recruitment_permit: recruitment_permit.value,
+ recruit_robot: recruit_robot.value,
+ recruit_auto_only5: recruit_auto_only5.value,
+ run_order_grandet_mode: run_order_grandet_mode.value,
+ check_mail_enable: check_mail_enable.value,
+ report_enable: report_enable.value,
+ recruit_gap: recruit_gap.value,
+ recruit_auto_5: recruit_auto_5.value,
+ webview: webview.value,
+ shop_collect_enable: shop_collect_enable.value ? 1 : 0,
+ meeting_level: meeting_level.value,
+ fix_mumu12_adb_disconnect: fix_mumu12_adb_disconnect.value,
+ reclamation_algorithm: {
+ timeout: ra_timeout.value
+ },
+ secret_front: {
+ target: sf_target.value
+ },
+ touch_method: touch_method.value,
+ free_room: free_room.value,
+ sign_in: sign_in.value,
+ droidcast: droidcast.value,
+ visit_friend: visit_friend.value,
+ credit_fight: credit_fight.value,
+ custom_screenshot: custom_screenshot.value,
+ check_for_updates: check_for_updates.value,
+ notification_level: notification_level.value,
+ waiting_scene: waiting_scene.value,
+ exipring_medicine_on_weekend: exipring_medicine_on_weekend.value
+ }
+ }
+
+ const loaded = inject('loaded')
+ watchEffect(() => {
+ if (loaded.value) {
+ axios.post(`${import.meta.env.VITE_HTTP_URL}/conf`, build_config())
+ }
+ })
+
+ return {
+ adb,
+ load_config,
+ drone_count_limit,
+ drone_room,
+ drone_interval,
+ enable_party,
+ leifeng_mode,
+ free_blacklist,
+ maa_adb_path,
+ maa_enable,
+ maa_path,
+ maa_rg_enable,
+ maa_long_task_type,
+ maa_expiring_medicine,
+ maa_weekly_plan,
+ maa_weekly_plan1,
+ mail_enable,
+ account,
+ pass_code,
+ recipient,
+ custom_smtp_server,
+ package_type,
+ reload_room,
+ run_order_delay,
+ start_automatically,
+ maa_mall_buy,
+ maa_mall_blacklist,
+ load_shop,
+ shop_list,
+ maa_gap,
+ build_config,
+ simulator,
+ resting_threshold,
+ theme,
+ tap_to_launch_game,
+ exit_game_when_idle,
+ close_simulator_when_idle,
+ maa_conn_preset,
+ maa_touch_option,
+ maa_mall_ignore_blacklist_when_full,
+ maa_rg_sleep_min,
+ maa_rg_sleep_max,
+ maa_credit_fight,
+ maa_depot_enable,
+ maa_rg_theme,
+ rogue,
+ sss,
+ screenshot,
+ screenshot_interval,
+ mail_subject,
+ recruit_enable,
+ recruitment_permit,
+ recruit_robot,
+ recruit_auto_only5,
+ skland_enable,
+ skland_info,
+ run_order_grandet_mode,
+ check_mail_enable,
+ report_enable,
+ recruit_gap,
+ recruit_auto_5,
+ webview,
+ shop_collect_enable,
+ meeting_level,
+ fix_mumu12_adb_disconnect,
+ ra_timeout,
+ sf_target,
+ touch_method,
+ free_room,
+ sign_in,
+ droidcast,
+ visit_friend,
+ credit_fight,
+ custom_screenshot,
+ check_for_updates,
+ notification_level,
+ waiting_scene,
+ exipring_medicine_on_weekend
+ }
+})
diff --git a/ui/src/stores/depot.js b/ui/src/stores/depot.js
new file mode 100644
index 000000000..f494b7400
--- /dev/null
+++ b/ui/src/stores/depot.js
@@ -0,0 +1,13 @@
+import { defineStore } from 'pinia'
+import axios from 'axios'
+
+export const usedepotStore = defineStore('depot', () => {
+ async function getDepotinfo() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/depot/readdepot`)
+ return response.data
+ }
+
+ return {
+ getDepotinfo
+ }
+})
diff --git a/ui/src/stores/mower.js b/ui/src/stores/mower.js
new file mode 100644
index 000000000..155eff1c3
--- /dev/null
+++ b/ui/src/stores/mower.js
@@ -0,0 +1,79 @@
+import { defineStore } from 'pinia'
+import ReconnectingWebSocket from 'reconnecting-websocket'
+import { computed, ref } from 'vue'
+
+import axios from 'axios'
+
+export const useMowerStore = defineStore('mower', () => {
+ const log_lines = ref([])
+
+ const log = computed(() => {
+ return log_lines.value.join('\n')
+ })
+
+ const num_reg = /^[0-9].*/
+ const log_mobile = computed(() => {
+ const result = []
+ for (const i of log_lines.value) {
+ if (i.match(num_reg)) {
+ result.push(i.substring(24))
+ } else {
+ result.push(i)
+ }
+ }
+ return result.join('\n')
+ })
+
+ const ws = ref(null)
+ const running = ref(false)
+ const waiting = ref(false)
+
+ const first_load = ref(true)
+
+ const get_task_id = ref(0)
+ const task_list = ref([])
+
+ function listen_ws() {
+ let backend_url
+ if (import.meta.env.DEV) {
+ backend_url = import.meta.env.VITE_HTTP_URL
+ } else {
+ backend_url = location.origin
+ }
+ const ws_url = backend_url.replace(/^http/, 'ws') + '/log'
+ ws.value = new ReconnectingWebSocket(ws_url)
+ ws.value.onmessage = (event) => {
+ log_lines.value = log_lines.value.concat(event.data.split('\n')).slice(-100)
+ }
+ }
+
+ async function get_running() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/running`)
+ running.value = response.data
+ }
+
+ async function get_tasks() {
+ if (running.value) {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/task`)
+ task_list.value = response.data
+ get_task_id.value = setTimeout(get_tasks, 3000)
+ } else {
+ task_list.value = []
+ }
+ }
+
+ return {
+ log,
+ log_mobile,
+ log_lines,
+ ws,
+ running,
+ waiting,
+ listen_ws,
+ get_running,
+ first_load,
+ task_list,
+ get_task_id,
+ get_tasks
+ }
+})
diff --git a/ui/src/stores/plan.js b/ui/src/stores/plan.js
new file mode 100644
index 000000000..d8785b57f
--- /dev/null
+++ b/ui/src/stores/plan.js
@@ -0,0 +1,255 @@
+import { defineStore } from 'pinia'
+import { ref, watchEffect, computed, inject } from 'vue'
+import axios from 'axios'
+import { deepcopy } from '@/utils/deepcopy'
+
+export const usePlanStore = defineStore('plan', () => {
+ const ling_xi = ref(1)
+ const max_resting_count = ref([])
+ const exhaust_require = ref([])
+ const rest_in_full = ref([])
+ const resting_priority = ref([])
+ const workaholic = ref([])
+ const refresh_trading = ref([])
+
+ const plan = ref({})
+
+ const backup_plans = ref([])
+
+ const operators = ref([])
+
+ const left_side_facility = []
+
+ const facility_operator_limit = {
+ central: 5,
+ meeting: 2,
+ factory: 1,
+ contact: 1,
+ train: 2
+ }
+ for (let i = 1; i <= 3; ++i) {
+ for (let j = 1; j <= 3; ++j) {
+ const facility_name = `room_${i}_${j}`
+ const display_name = `B${i}0${j}`
+ facility_operator_limit[facility_name] = 3
+ left_side_facility.push({ label: display_name, value: facility_name })
+ }
+ }
+ for (let i = 1; i <= 4; ++i) {
+ facility_operator_limit[`dormitory_${i}`] = 5
+ }
+
+ function list2str(data) {
+ return data.join(',')
+ }
+
+ function str2list(data) {
+ return data && data != '' ? data.split(',') : []
+ }
+
+ const backup_conf_convert_list = [
+ 'exhaust_require',
+ 'rest_in_full',
+ 'resting_priority',
+ 'workaholic',
+ 'free_blacklist',
+ 'refresh_trading'
+ ]
+
+ function fill_empty(full_plan) {
+ for (const i in facility_operator_limit) {
+ let count = 0
+ if (!full_plan[i]) {
+ count = facility_operator_limit[i]
+ full_plan[i] = { name: '', plans: [] }
+ } else {
+ let limit = facility_operator_limit[i]
+ if (full_plan[i].name == '发电站') {
+ limit = 1
+ } else if (full_plan[i].name == '贸易站') {
+ if (!['lmd', 'orundum'].includes(full_plan[i].product)) {
+ full_plan[i].product = 'lmd'
+ }
+ } else if (full_plan[i].name == '制造站') {
+ if (!['gold', 'exp3', 'orirock'].includes(full_plan[i].product)) {
+ full_plan[i].product = 'gold'
+ }
+ }
+ if (full_plan[i].plans.length < limit) {
+ count = limit - full_plan[i].plans.length
+ }
+ }
+ for (let j = 0; j < count; ++j) {
+ full_plan[i].plans.push({ agent: '', group: '', replacement: [] })
+ }
+ }
+ return full_plan
+ }
+
+ function remove_empty_agent(input) {
+ const result = {
+ name: input.name,
+ plans: []
+ }
+ if (['贸易站', '制造站'].includes(input.name)) {
+ result.product = input.product
+ }
+ for (const i of input.plans) {
+ if (i.agent) {
+ result.plans.push(i)
+ }
+ }
+ return result
+ }
+
+ function strip_plan(plan) {
+ const plan1 = {}
+
+ for (const i in facility_operator_limit) {
+ if (i.startsWith('room') && plan[i].name) {
+ plan1[i] = remove_empty_agent(plan[i])
+ } else {
+ let empty = true
+ for (const j of plan[i].plans) {
+ if (j.agent) {
+ empty = false
+ break
+ }
+ }
+ if (!empty) {
+ plan1[i] = remove_empty_agent(plan[i])
+ }
+ }
+ }
+
+ return plan1
+ }
+
+ async function load_plan() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/plan`)
+ ling_xi.value = response.data.conf.ling_xi
+ max_resting_count.value = response.data.conf.max_resting_count
+ exhaust_require.value = str2list(response.data.conf.exhaust_require)
+ rest_in_full.value = str2list(response.data.conf.rest_in_full)
+ resting_priority.value = str2list(response.data.conf.resting_priority)
+ workaholic.value = str2list(response.data.conf.workaholic)
+ refresh_trading.value = str2list(response.data.conf.refresh_trading)
+
+ plan.value = fill_empty(response.data.plan1)
+
+ backup_plans.value = response.data.backup_plans ?? []
+ for (let b of backup_plans.value) {
+ for (const i of backup_conf_convert_list) {
+ b.conf[i] = str2list(b.conf[i])
+ }
+ b.plan = fill_empty(b.plan)
+ }
+ }
+
+ async function load_operators() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/operator`)
+ const option_list = []
+ for (const i of response.data) {
+ option_list.push({
+ value: i,
+ label: i
+ })
+ }
+ operators.value = option_list
+ }
+
+ function build_plan() {
+ const result = {
+ default: 'plan1',
+ plan1: strip_plan(plan.value),
+ conf: {
+ ling_xi: ling_xi.value,
+ max_resting_count: max_resting_count.value,
+ exhaust_require: list2str(exhaust_require.value),
+ rest_in_full: list2str(rest_in_full.value),
+ resting_priority: list2str(resting_priority.value),
+ workaholic: list2str(workaholic.value),
+ refresh_trading: list2str(refresh_trading.value)
+ },
+ backup_plans: deepcopy(backup_plans.value)
+ }
+ for (const b of result.backup_plans) {
+ for (const i of backup_conf_convert_list) {
+ b.conf[i] = list2str(b.conf[i])
+ }
+ b.plan = strip_plan(b.plan)
+ }
+
+ const plan1 = result.plan1
+
+ for (const i in facility_operator_limit) {
+ if (i.startsWith('room') && plan.value[i].name) {
+ plan1[i] = remove_empty_agent(plan.value[i])
+ } else {
+ let empty = true
+ for (const j of plan.value[i].plans) {
+ if (j.agent) {
+ empty = false
+ break
+ }
+ }
+ if (!empty) {
+ plan1[i] = remove_empty_agent(plan.value[i])
+ }
+ }
+ }
+
+ return result
+ }
+
+ const loaded = inject('loaded')
+
+ watchEffect(() => {
+ if (loaded.value) {
+ axios.post(`${import.meta.env.VITE_HTTP_URL}/plan`, build_plan())
+ }
+ })
+
+ const groups = computed(() => {
+ const result = []
+ for (const facility in plan.value) {
+ for (const p of plan.value[facility].plans) {
+ if (p.group) {
+ result.push(p.group)
+ }
+ }
+ }
+ return [...new Set(result)]
+ })
+
+ const sub_plan = ref('main')
+ const current_plan = computed(() => {
+ if (sub_plan.value == 'main') {
+ return plan.value
+ } else {
+ return backup_plans.value[sub_plan.value].plan
+ }
+ })
+
+ return {
+ load_plan,
+ load_operators,
+ ling_xi,
+ max_resting_count,
+ exhaust_require,
+ rest_in_full,
+ resting_priority,
+ workaholic,
+ refresh_trading,
+ plan,
+ operators,
+ facility_operator_limit,
+ left_side_facility,
+ build_plan,
+ groups,
+ backup_plans,
+ sub_plan,
+ current_plan,
+ fill_empty
+ }
+})
diff --git a/ui/src/stores/record.js b/ui/src/stores/record.js
new file mode 100644
index 000000000..968853c3e
--- /dev/null
+++ b/ui/src/stores/record.js
@@ -0,0 +1,13 @@
+import { defineStore } from 'pinia'
+import axios from 'axios'
+
+export const useRecordStore = defineStore('record', () => {
+ async function getMoodRatios() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/record/getMoodRatios`)
+ return response.data
+ }
+
+ return {
+ getMoodRatios
+ }
+})
diff --git a/ui/src/stores/report.js b/ui/src/stores/report.js
new file mode 100644
index 000000000..b0fae0ead
--- /dev/null
+++ b/ui/src/stores/report.js
@@ -0,0 +1,19 @@
+import { defineStore } from 'pinia'
+import axios from 'axios'
+
+export const useReportStore = defineStore('report', () => {
+ async function getReportData() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/report/getReportData`)
+ console.log(response.data)
+ return response.data
+ }
+ async function getOrundumData() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/report/getOrundumData`)
+ console.log(response.data)
+ return response.data
+ }
+ return {
+ getReportData,
+ getOrundumData
+ }
+})
diff --git a/ui/src/stores/richText2HTML.js b/ui/src/stores/richText2HTML.js
new file mode 100644
index 000000000..adf027cfe
--- /dev/null
+++ b/ui/src/stores/richText2HTML.js
@@ -0,0 +1,30 @@
+const getClassName = (str) => str.replace(/^[^0-9a-zA-Z]/, '').replace(/[^0-9a-zA-Z]/g, '-')
+const getTermId = (str) => str.replace(/^\W/, '').replace(/\W/g, '_')
+
+export const richText2HTML = (text) => {
+ const result = text
+ .replace(/<([^<>]+)>([^<>]+)<\/>/g, (str, key, value) => {
+ if (key.startsWith('@cc.')) {
+ return `{{span class="riic-rt ${getClassName(key)}"}}${value}{{/span}}`
+ }
+ if (key.startsWith('$cc.')) {
+ return `{{span class="riic-term" data-id="${getTermId(key)}"}}${value}{{/span}}`
+ }
+ })
+ .replace(/\n/g, '
')
+ return /<[^<>]+>[^<>]+<\/>/.test(result)
+ ? richText2HTML(result)
+ : result.replace(/{{/g, '<').replace(/}}/g, '>')
+}
+
+export const removeRichTextTag = (str) => {
+ const result = str.replace(/<(?:[^>]+)>([^<>]+)<\/>/g, '$1')
+ return /<[^>]+>[^<>]+<\/>/.test(result) ? removeRichTextTag(result) : result
+}
+
+export const findTerm = (e, stopFn = () => false) => {
+ for (const el of e.composedPath?.() || []) {
+ if (stopFn(el)) return
+ if (el.classList?.contains('riic-term')) return el
+ }
+}
diff --git a/ui/src/stores/watermark.js b/ui/src/stores/watermark.js
new file mode 100644
index 000000000..b1786aed6
--- /dev/null
+++ b/ui/src/stores/watermark.js
@@ -0,0 +1,13 @@
+import { defineStore } from 'pinia'
+import axios from 'axios'
+
+export const usewatermarkStore = defineStore('watermark', () => {
+ async function getwatermarkinfo() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/getwatermark`)
+ return response.data.toString()
+ }
+
+ return {
+ getwatermarkinfo
+ }
+})
diff --git a/ui/src/utils/common.js b/ui/src/utils/common.js
new file mode 100644
index 000000000..2075fbc82
--- /dev/null
+++ b/ui/src/utils/common.js
@@ -0,0 +1,12 @@
+export function swap(source, target, arr) {
+ const source_plan = arr[source]
+ arr[source] = arr[target]
+ arr[target] = source_plan
+}
+
+import { match } from 'pinyin-pro'
+
+export function pinyin_match(text, pinyin) {
+ pinyin = pinyin.replaceAll('v', 'ü')
+ return match(text, pinyin)
+}
diff --git a/ui/src/utils/deepcopy.js b/ui/src/utils/deepcopy.js
new file mode 100644
index 000000000..8a9a11aec
--- /dev/null
+++ b/ui/src/utils/deepcopy.js
@@ -0,0 +1,3 @@
+export function deepcopy(data) {
+ return JSON.parse(JSON.stringify(data))
+}
diff --git a/ui/src/utils/dialog.js b/ui/src/utils/dialog.js
new file mode 100644
index 000000000..3f7c4bbcd
--- /dev/null
+++ b/ui/src/utils/dialog.js
@@ -0,0 +1,11 @@
+import axios from 'axios'
+
+export async function file_dialog() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/dialog/file`)
+ return response.data
+}
+
+export async function folder_dialog() {
+ const response = await axios.get(`${import.meta.env.VITE_HTTP_URL}/dialog/folder`)
+ return response.data
+}
diff --git a/ui/src/utils/op_select.js b/ui/src/utils/op_select.js
new file mode 100644
index 000000000..fe4c2fa75
--- /dev/null
+++ b/ui/src/utils/op_select.js
@@ -0,0 +1,63 @@
+import { NAvatar, NTag } from 'naive-ui'
+import { h } from 'vue'
+
+export const render_op_tag = ({ option, handleClose }) => {
+ return h(
+ NTag,
+ {
+ style: {
+ padding: '0 6px 0 4px'
+ },
+ round: true,
+ closable: true,
+ onClose: (e) => {
+ e.stopPropagation()
+ handleClose()
+ }
+ },
+ {
+ default: () =>
+ h(
+ 'div',
+ {
+ style: {
+ display: 'flex',
+ alignItems: 'center'
+ }
+ },
+ [
+ h(NAvatar, {
+ src: 'avatar/' + option.value + '.webp',
+ round: true,
+ size: 22,
+ style: {
+ marginRight: '4px'
+ }
+ }),
+ option.label
+ ]
+ )
+ }
+ )
+}
+
+export const render_op_label = (option) => {
+ return h(
+ 'div',
+ {
+ style: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '12px'
+ }
+ },
+ [
+ h(NAvatar, {
+ src: 'avatar/' + option.value + '.webp',
+ round: true,
+ size: 'small'
+ }),
+ option.label
+ ]
+ )
+}
diff --git a/ui/src/utils/sleep.js b/ui/src/utils/sleep.js
new file mode 100644
index 000000000..c6c732fe3
--- /dev/null
+++ b/ui/src/utils/sleep.js
@@ -0,0 +1,3 @@
+export function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms))
+}
diff --git a/ui/vite.config.js b/ui/vite.config.js
new file mode 100644
index 000000000..76a8618a4
--- /dev/null
+++ b/ui/vite.config.js
@@ -0,0 +1,43 @@
+import { fileURLToPath, URL } from 'node:url'
+import { resolve } from 'path'
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import Inspect from 'vite-plugin-inspect'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ Inspect(),
+ vue(),
+ vueJsx(),
+ AutoImport({
+ imports: [
+ 'vue',
+ {
+ 'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar']
+ }
+ ]
+ }),
+ Components({
+ resolvers: [NaiveUiResolver()]
+ })
+ ],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ },
+ build: {
+ rollupOptions: {
+ input: {
+ main: resolve(__dirname, 'index.html'),
+ manager: resolve(__dirname, 'manager/index.html')
+ }
+ }
+ }
+})
diff --git a/webui_zip.spec b/webui_zip.spec
new file mode 100644
index 000000000..35887ca82
--- /dev/null
+++ b/webui_zip.spec
@@ -0,0 +1,142 @@
+# -*- mode: python ; coding: utf-8 -*-
+from pathlib import Path
+
+import rapidocr_onnxruntime
+
+block_cipher = None
+
+# 参考 https://github.com/RapidAI/RapidOCR/blob/main/ocrweb/rapidocr_web/ocrweb.spec
+package_name = "rapidocr_onnxruntime"
+install_dir = Path(rapidocr_onnxruntime.__file__).resolve().parent
+
+onnx_paths = list(install_dir.rglob("*.onnx"))
+yaml_paths = list(install_dir.rglob("*.yaml"))
+
+onnx_add_data = [(str(v.parent), f"{package_name}/{v.parent.name}") for v in onnx_paths]
+
+yaml_add_data = []
+for v in yaml_paths:
+ if package_name == v.parent.name:
+ yaml_add_data.append((str(v.parent / "*.yaml"), package_name))
+ else:
+ yaml_add_data.append(
+ (str(v.parent / "*.yaml"), f"{package_name}/{v.parent.name}")
+ )
+
+add_data = list(set(yaml_add_data + onnx_add_data))
+
+
+site_packages = install_dir.parent
+
+
+mower_a = Analysis(
+ ["webview_ui.py"],
+ pathex=[],
+ binaries=[],
+ datas=[
+ ("arknights_mower", "arknights_mower"),
+ ("logo.png", "."),
+ (
+ f"{site_packages}/onnxruntime/capi/onnxruntime_providers_shared.dll",
+ "onnxruntime/capi/",
+ ),
+ (f"{site_packages}/pyzbar/libzbar-64.dll", "."),
+ (f"{site_packages}/pyzbar/libiconv.dll", "."),
+ ]
+ + add_data,
+ hiddenimports=[],
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher,
+ noarchive=False,
+)
+
+mower_pure = [i for i in mower_a.pure if not i[0].startswith("arknights_mower")]
+
+mower_pyz = PYZ(
+ mower_pure,
+ mower_a.zipped_data,
+ cipher=block_cipher,
+)
+
+
+mower_exe = EXE(
+ mower_pyz,
+ mower_a.scripts,
+ [],
+ exclude_binaries=True,
+ name="mower",
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ console=False,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+ icon="logo.ico",
+)
+
+
+manager_a = Analysis(
+ ["manager.py"],
+ pathex=[],
+ binaries=[],
+ datas=[],
+ hiddenimports=[],
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher,
+ noarchive=False,
+)
+
+manager_pyz = PYZ(
+ manager_a.pure,
+ manager_a.zipped_data,
+ cipher=block_cipher,
+)
+
+manager_exe = EXE(
+ manager_pyz,
+ manager_a.scripts,
+ [],
+ exclude_binaries=True,
+ name="多开管理器",
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ console=False,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+ icon="logo.ico",
+)
+
+
+coll = COLLECT(
+ mower_exe,
+ mower_a.binaries,
+ mower_a.zipfiles,
+ mower_a.datas,
+ manager_exe,
+ manager_a.binaries,
+ manager_a.zipfiles,
+ manager_a.datas,
+ strip=False,
+ upx=True,
+ upx_exclude=[],
+ name="mower",
+)
diff --git a/webview_ui.py b/webview_ui.py
new file mode 100755
index 000000000..22ce285f0
--- /dev/null
+++ b/webview_ui.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+import multiprocessing as mp
+
+
+def splash_screen(queue: mp.Queue):
+ import tkinter as tk
+ from tkinter.font import Font
+
+ from PIL import Image, ImageTk
+
+ from arknights_mower.utils.path import get_path
+
+ root = tk.Tk()
+ container = tk.Frame(root)
+
+ logo_path = get_path("@internal/logo.png")
+ img = Image.open(logo_path)
+ img = ImageTk.PhotoImage(img)
+ canvas = tk.Canvas(container, width=256, height=256)
+ canvas.create_image(128, 128, image=img)
+ canvas.pack()
+
+ title_font = Font(size=24)
+ title_label = tk.Label(
+ container,
+ text="arknights-mower",
+ font=title_font,
+ )
+ title_label.pack()
+
+ loading_label = tk.Label(container)
+ loading_label.pack()
+
+ container.pack(expand=1)
+ root.overrideredirect(True)
+
+ window_width = 500
+ window_height = 400
+ screen_width = root.winfo_screenwidth()
+ screen_height = root.winfo_screenheight()
+ x = int(screen_width / 2 - window_width / 2)
+ y = int(screen_height / 2 - window_height / 2)
+ root.geometry(f"{window_width}x{window_height}+{x}+{y}")
+
+ def recv_msg():
+ try:
+ msg = queue.get(False)
+ if msg["type"] == "text":
+ loading_label.config(text=msg["data"] + "……")
+ root.after(100, recv_msg)
+ elif msg["type"] == "dialog":
+ from tkinter import messagebox
+
+ root.withdraw()
+ messagebox.showerror("arknights-mower", msg["data"])
+ root.destroy()
+ except Exception:
+ pass
+
+ root.after(100, recv_msg)
+ root.mainloop()
+
+
+def start_tray(queue: mp.Queue, global_space, port, url):
+ from PIL import Image
+ from pystray import Icon, Menu, MenuItem
+
+ from arknights_mower.utils.path import get_path
+
+ logo_path = get_path("@internal/logo.png")
+ img = Image.open(logo_path)
+
+ title = f"mower@{port}({global_space})" if global_space else f"mower@{port}"
+
+ def open_browser():
+ import webbrowser
+
+ webbrowser.open(url)
+
+ icon = Icon(
+ name="arknights-mower",
+ icon=img,
+ menu=Menu(
+ MenuItem(
+ text=title,
+ action=None,
+ enabled=False,
+ ),
+ Menu.SEPARATOR,
+ MenuItem(
+ text="打开/关闭窗口",
+ action=lambda: queue.put("toggle"),
+ default=True,
+ ),
+ MenuItem(
+ text="在浏览器中打开网页面板",
+ action=open_browser,
+ ),
+ Menu.SEPARATOR,
+ MenuItem(
+ text="退出",
+ action=lambda: queue.put("exit"),
+ ),
+ ),
+ title=title,
+ )
+ icon.run()
+
+
+def webview_window(child_conn, global_space, host, port, url, tray):
+ import sys
+ from threading import Thread
+
+ import webview
+
+ webview.settings["ALLOW_DOWNLOADS"] = True
+
+ from arknights_mower.__init__ import __version__
+ from arknights_mower.utils import config, path
+
+ path.global_space = global_space
+
+ global width
+ global height
+
+ config.load_conf()
+ width = config.conf.webview.width
+ height = config.conf.webview.height
+
+ def window_size(w, h):
+ global width
+ global height
+ width = w
+ height = h
+
+ window = webview.create_window(
+ f"arknights-mower {__version__} (http://{host}:{port})",
+ url,
+ text_select=True,
+ confirm_close=not tray,
+ width=width,
+ height=height,
+ )
+ window.events.resized += window_size
+
+ def recv_msg():
+ while True:
+ msg = child_conn.recv()
+ if msg == "exit":
+ window.confirm_close = False
+ window.destroy()
+ return
+ if msg == "file":
+ result = window.create_file_dialog(
+ dialog_type=webview.OPEN_DIALOG,
+ )
+ elif msg == "folder":
+ result = window.create_file_dialog(
+ dialog_type=webview.FOLDER_DIALOG,
+ )
+ if result is None:
+ result = ""
+ elif not isinstance(result, str):
+ if len(result) == 0:
+ result = ""
+ else:
+ result = result[0]
+ child_conn.send(result)
+
+ Thread(target=recv_msg, daemon=True).start()
+
+ try:
+ webview.start()
+
+ config.load_conf()
+ config.conf.webview.width = width
+ config.conf.webview.height = height
+ config.save_conf()
+ sys.exit()
+ except Exception:
+ import webbrowser
+
+ webbrowser.open(url)
+
+
+if __name__ == "__main__":
+ mp.freeze_support()
+
+ splash_queue = mp.Queue()
+ splash_process = mp.Process(target=splash_screen, args=(splash_queue,), daemon=True)
+ splash_process.start()
+
+ splash_queue.put({"type": "text", "data": "加载配置文件"})
+
+ import sys
+
+ from arknights_mower.utils import path
+
+ if len(sys.argv) == 2:
+ path.global_space = sys.argv[1]
+
+ from arknights_mower.utils import config
+
+ conf = config.conf
+ tray = conf.webview.tray
+ token = conf.webview.token
+ host = "0.0.0.0" if token else "127.0.0.1"
+
+ splash_queue.put({"type": "text", "data": "检测端口占用"})
+
+ from arknights_mower.utils.network import get_new_port, is_port_in_use
+
+ if token:
+ port = conf.webview.port
+
+ if is_port_in_use(port):
+ splash_queue.put(
+ {"type": "dialog", "data": f"端口{port}已被占用,无法启动!"}
+ )
+ sys.exit()
+ else:
+ port = get_new_port()
+
+ url = f"http://127.0.0.1:{port}"
+ if token:
+ url += f"?token={token}"
+
+ splash_queue.put({"type": "text", "data": "加载Flask依赖"})
+
+ from server import app
+
+ splash_queue.put({"type": "text", "data": "启动Flask网页服务器"})
+
+ from threading import Thread
+ from time import sleep
+
+ if token:
+ app.token = token
+ flask_thread = Thread(
+ target=app.run,
+ kwargs={"host": host, "port": port},
+ daemon=True,
+ )
+ flask_thread.start()
+
+ while not is_port_in_use(port):
+ sleep(0.1)
+
+ url = f"http://127.0.0.1:{port}"
+ if token:
+ url += f"?token={token}"
+
+ if tray:
+ splash_queue.put({"type": "text", "data": "加载托盘图标"})
+ tray_queue = mp.Queue()
+ tray_process = mp.Process(
+ target=start_tray,
+ args=(tray_queue, path.global_space, port, url),
+ daemon=True,
+ )
+ tray_process.start()
+
+ splash_queue.put({"type": "text", "data": "创建主窗口"})
+
+ config.parent_conn, child_conn = mp.Pipe()
+ config.webview_process = mp.Process(
+ target=webview_window,
+ args=(child_conn, path.global_space, host, port, url, tray),
+ daemon=True,
+ )
+ config.webview_process.start()
+
+ splash_process.terminate()
+
+ if tray:
+ while True:
+ msg = tray_queue.get()
+ if msg == "toggle":
+ if config.webview_process.is_alive():
+ config.parent_conn.send("exit")
+ if config.webview_process.join(3) is None:
+ config.webview_process.terminate()
+ else:
+ config.parent_conn, child_conn = mp.Pipe()
+ config.webview_process = mp.Process(
+ target=webview_window,
+ args=(
+ child_conn,
+ path.global_space,
+ host,
+ port,
+ url,
+ tray,
+ ),
+ daemon=True,
+ )
+ config.webview_process.start()
+ elif msg == "exit":
+ config.parent_conn.send("exit")
+ if config.webview_process.join(3) is None:
+ config.webview_process.terminate()
+ break
+ else:
+ config.webview_process.join()
diff --git "a/\346\230\216\346\227\245\346\226\271\350\210\237\346\243\256\347\251\272\345\262\233\346\225\260\346\215\256.py" "b/\346\230\216\346\227\245\346\226\271\350\210\237\346\243\256\347\251\272\345\262\233\346\225\260\346\215\256.py"
new file mode 100644
index 000000000..8afbf54d8
--- /dev/null
+++ "b/\346\230\216\346\227\245\346\226\271\350\210\237\346\243\256\347\251\272\345\262\233\346\225\260\346\215\256.py"
@@ -0,0 +1,176 @@
+import hashlib
+import hmac
+import json
+import time
+from urllib import parse
+
+import requests
+
+app_code = "4ca99fa6b56cc2ba"
+
+# 签到url
+sign_url = "https://zonai.skland.com/api/v1/game/attendance"
+# 绑定的角色url
+binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
+# 验证码url
+login_code_url = "https://as.hypergryph.com/general/v1/send_phone_code"
+# 验证码登录
+token_phone_code_url = "https://as.hypergryph.com/user/auth/v2/token_by_phone_code"
+# 密码登录
+token_password_url = "https://as.hypergryph.com/user/auth/v1/token_by_phone_password"
+# 使用token获得认证代码
+grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
+# 使用认证代码获得cred
+cred_code_url = "https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code"
+
+
+class SKLand:
+ def __init__(
+ self,
+ ):
+ self.record_path = "./skland.csv"
+ self.account_list = []
+
+ self.account_list.append(
+ {
+ "account": "account",
+ "isCheck": True,
+ "password": "password",
+ }
+ )
+ self.header = {
+ "cred": "",
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+ self.header_login = {
+ "User-Agent": "Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 31; ) Okhttp/4.11.0",
+ "Accept-Encoding": "gzip",
+ "Connection": "close",
+ }
+
+ self.reward = []
+ # 签名请求头一定要这个顺序,否则失败
+ # timestamp是必填的,其它三个随便填,不要为none即可
+ self.header_for_sign = {"platform": "", "timestamp": "", "dId": "", "vName": ""}
+ self.sign_token = ""
+ self.all_recorded = True
+
+ def start(self):
+ for item in self.account_list:
+ self.all_recorded = False
+ self.save_param(self.get_cred_by_token(self.log(item)))
+ for i in self.get_binding_list():
+ body = {"gameId": 1, "uid": i.get("uid")}
+ ingame = f"https://zonai.skland.com/api/v1/game/player/info?uid={i.get('uid')}"
+ resp = requests.get(
+ ingame,
+ headers=self.get_sign_header(ingame, "get", body, self.header),
+ ).json()
+ with open("森空岛数据.json", "w", encoding="utf-8") as 保存:
+ json.dump(resp, 保存, ensure_ascii=False, indent=4)
+ print(resp["data"]["chars"])
+
+ def save_param(self, cred_resp):
+ self.header["cred"] = cred_resp["cred"]
+ self.sign_token = cred_resp["token"]
+
+ def log(self, account):
+ r = requests.post(
+ token_password_url,
+ json={"phone": account["account"], "password": account["password"]},
+ headers=self.header_login,
+ ).json()
+ if r.get("status") != 0:
+ raise Exception(f'获得token失败:{r["msg"]}')
+ return r["data"]["token"]
+
+ def get_cred_by_token(self, token):
+ return self.get_cred(self.get_grant_code(token))
+
+ def get_grant_code(self, token):
+ response = requests.post(
+ grant_code_url,
+ json={"appCode": app_code, "token": token, "type": 0},
+ headers=self.header_login,
+ )
+ resp = response.json()
+ if response.status_code != 200:
+ raise Exception(f"获得认证代码失败:{resp}")
+ if resp.get("status") != 0:
+ raise Exception(f'获得认证代码失败:{resp["msg"]}')
+ return resp["data"]["code"]
+
+ def get_cred(self, grant):
+ resp = requests.post(
+ cred_code_url, json={"code": grant, "kind": 1}, headers=self.header_login
+ ).json()
+ if resp["code"] != 0:
+ raise Exception(f'获得cred失败:{resp["message"]}')
+ return resp["data"]
+
+ def get_binding_list(self):
+ v = []
+ resp = requests.get(
+ binding_url,
+ headers=self.get_sign_header(binding_url, "get", None, self.header),
+ ).json()
+
+ if resp["code"] != 0:
+ print(f"请求角色列表出现问题:{resp['message']}")
+ if resp.get("message") == "用户未登录":
+ print("用户登录可能失效了,请重新运行此程序!")
+ return []
+ for i in resp["data"]["list"]:
+ if i.get("appCode") != "arknights":
+ continue
+ v.extend(i.get("bindingList"))
+ return v
+
+ def get_sign_header(self, url: str, method, body, old_header):
+ h = json.loads(json.dumps(old_header))
+ p = parse.urlparse(url)
+ if method.lower() == "get":
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, p.query
+ )
+ else:
+ h["sign"], header_ca = self.generate_signature(
+ self.sign_token, p.path, json.dumps(body)
+ )
+ for i in header_ca:
+ h[i] = header_ca[i]
+ return h
+
+ def generate_signature(self, token: str, path, body_or_query):
+ """
+ 获得签名头
+ 接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dId,platform,timestamp,vName).toJSON()
+ 将此字符串做HMAC加密,算法为SHA-256,密钥token为请求cred接口会返回的一个token值
+ 再将加密后的字符串做MD5即得到sign
+ :param token: 拿cred时候的token
+ :param path: 请求路径(不包括网址)
+ :param body_or_query: 如果是GET,则是它的query。POST则为它的body
+ :return: 计算完毕的sign
+ """
+ # 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2
+
+ t = str(int(time.time()) - 2)
+ token = token.encode("utf-8")
+ header_ca = json.loads(json.dumps(self.header_for_sign))
+ header_ca["timestamp"] = t
+ header_ca_str = json.dumps(header_ca, separators=(",", ":"))
+ s = path + body_or_query + t + header_ca_str
+ hex_s = hmac.new(token, s.encode("utf-8"), hashlib.sha256).hexdigest()
+ md5 = (
+ hashlib.md5(hex_s.encode("utf-8"))
+ .hexdigest()
+ .encode("utf-8")
+ .decode("utf-8")
+ )
+ return md5, header_ca
+
+
+a = SKLand()
+a.start()