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 %} + + + + + {% endfor %} + + + + +
#掉落物品
{{ loop.index }} + {% for item in drop %}{{ item.itemName }}x{{ item.quantity }}{% if not + loop.last %},{% endif %}{% 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 %} + + + + + + {% endfor %} + +
标签选择预计干员名单
+ {{ result['tag'] }} + + {% for agent in result['result'] %} + {{ agent.name }} + {% 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() %} + + + + + + + + {% endfor %} {% endif %} {% if recruit_get_agent|length > 0 %} + + + + {% for pos,value in recruit_get_agent.items() %} + + + + + {% endfor %} {% endif %} + + + {% if permit_count is defined%} + + + {% endif %} + + + +
位置标签预计干员
{{ pos }} + {% if value.tags|length > 0 %} + {% for tag in value.tags %}{{ tag }}{% endfor %} + {% else %} + 未选择 + {% endif %} + + + {% for agent in value.result %} + {{ agent.name }} + {% endfor %} +
上次公招干员
{{ pos }}{{ value }}
剩余招募券数量{{permit_count}}
+ + 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 %} + + {% endif %} + {% if task.type == '释放宿舍空位'%} + + {% else %} + + {% endif %} + + + {% endfor %} + {% elif task.type %} + + + + + {% else %} + + + + + {% endif %} + {% endfor %} +
时间任务
{{ task.time.strftime('%H:%M:%S') }}{{ task.meta_data }}在{{ base_scheduler.translate_room(r) }}休息完毕{{ base_scheduler.translate_room(r) }}{{ p|join(', ') }}
{{ task.time.strftime('%H:%M:%S') }}{{ task.type }}
{{ task.time.strftime('%H:%M:%S') }}空任务
+ + 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版本:

+

稳定版:

+ +

测试版:

+ + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + 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 @@ + + + + + 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 @@ + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + 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 @@ +