改写战斗中替换group的逻辑

This commit is contained in:
Elaina 2024-10-13 01:24:58 +08:00
commit 7f89eb0db8
3890 changed files with 82290 additions and 0 deletions

6
.gitattributes vendored Normal file
View file

@ -0,0 +1,6 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
mower/utils/vendor/* filter=lfs diff=lfs merge=lfs -text

210
.gitignore vendored Normal file
View file

@ -0,0 +1,210 @@
FZDYSK.TTF
.vscode/
!main.spec
*.yaml
!mower/templates/*.yaml
# WSL
log/*
data/*
screenshot/*
screenshot_right/*
test/*
test_*
release.sh
# Windows
venv64
release_test.bat
publish/*
# Myself
test.py
cron.py
cron.json
cron.bak.json
cute.json
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Nodejs Version Manager
.nvmrc
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
/dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# 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
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
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
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env/
.venv/
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
###JetBrains Product###
.idea/*
### 命令行运行产生的/tmp/data.db
/tmp/
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
/*.json
MAA
/*.yml
# venv
/bin/
/pyvenv.cfg
/lib64
#testspace
/testspace
/.buildozer/
*.ipynb
.rgignore

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "ArknightsGameResource"]
path = ArknightsGameResource
url = https://github.com/yuanyan3060/ArknightsGameResource
[submodule "ArknightsGameData"]
path = ArknightsGameData
url = https://github.com/Kengxxiao/ArknightsGameData.git

View file

@ -0,0 +1,19 @@
when:
- event: push
branch:
exclude:
- fast
- slow
skip_clone: true
steps:
- name: check
image: git.zhaozuohong.vip/mower-ng/ci
environment:
GIT_LFS_SKIP_SMUDGE: "1"
commands:
- git clone ${CI_REPO_CLONE_URL} --branch ${CI_COMMIT_BRANCH} --depth 1 .
- ruff check
- ruff format --check
- prettier --check ui/**/*.js ui/**/*.vue

1
ArknightsGameData Submodule

@ -0,0 +1 @@
Subproject commit 1c77befbd52f4e1a436238afd181de8aeb6ed976

1
ArknightsGameResource Submodule

@ -0,0 +1 @@
Subproject commit 0fcc20c7a7aa42bf789d078d5ecc1cce3c9355a8

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 Nano
Copyright (c) 2024 mower-ng
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,67 @@
import ctypes
import time
import cv2
import numpy as np
# 加载动态链接库
external_renderer = ctypes.CDLL(
"C:\\Program Files\\NetEase\\MuMu Player 12\\shell\\sdk\\external_renderer_ipc.dll"
)
# 定义函数原型
external_renderer.nemu_connect.argtypes = [ctypes.c_wchar_p, ctypes.c_int]
external_renderer.nemu_connect.restype = ctypes.c_int
external_renderer.nemu_disconnect.argtypes = [ctypes.c_int]
external_renderer.nemu_disconnect.restype = None
external_renderer.nemu_capture_display.argtypes = [
ctypes.c_int,
ctypes.c_uint,
ctypes.c_int,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_ubyte),
]
external_renderer.nemu_capture_display.restype = ctypes.c_int
# 设置 emulator 路径和实例
install_path = ctypes.c_wchar_p("C:\\Program Files\\NetEase\\MuMu Player 12")
instance_index = 0
# 连接到 emulator
connection = external_renderer.nemu_connect(install_path, instance_index)
if connection == 0:
print("连接失败,请确认安装目录正确且实例已经打开")
else:
print("连接成功")
pixels = (ctypes.c_ubyte * 8294400)()
start_time = time.time()
external_renderer.nemu_capture_display(
connection,
0,
8294400,
ctypes.byref(ctypes.c_int(1920)),
ctypes.byref(ctypes.c_int(1080)),
pixels,
)
image = np.frombuffer(pixels, dtype=np.uint8).reshape((1080, 1920, 4))[
:, :, :3
] # RGBA-RGB
image = np.flipud(image) # 翻转
print(f"像素转换为矩阵:{time.time() - start_time:.4f}")
image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
print(f"OpenCV转换BGR,耗时:{time.time() - start_time:.4f}")
cv2.imwrite("screenshot.png", image_bgr)
print(f"截图已保存,耗时:{time.time() - start_time:.4f}")
# 断开连接
external_renderer.nemu_disconnect(connection)

690
auto_get_res_new.py Normal file
View file

@ -0,0 +1,690 @@
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 mower.utils.image import cropimg, loadimg, thres2
def 加载json(file_path):
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
class Arknights数据处理器:
def __init__(self):
self.当前时间戳 = datetime.now().timestamp()
拼接 = "ArknightsGameResource/gamedata/excel"
self.物品表 = 加载json(f"./{拼接}/item_table.json")
self.干员表 = 加载json(f"./{拼接}/character_table.json")
self.阿米娅表 = 加载json(f"./{拼接}/char_patch_table.json")
self.抽卡表 = 加载json(f"./{拼接}/gacha_table.json")
self.关卡表 = 加载json(f"./{拼接}/stage_table.json")
self.活动表 = 加载json(f"./{拼接}/activity_table.json")
self.基建表 = 加载json(f"./{拼接}/building_data.json")
self.游戏变量 = 加载json(f"./{拼接}/gamedata_const.json")
self.模组 = 加载json(f"./{拼接}/uniequip_table.json")
self.装仓库物品的字典 = {"NORMAL": [], "CONSUME": [], "MATERIAL": []}
self.常驻关卡 = 加载json("mower/data/stage_data.json")
self.所有buff = []
self.限定十连 = self.抽卡表["limitTenGachaItem"]
self.联动十连 = self.抽卡表["linkageTenGachaItem"]
self.普通十连 = self.抽卡表["normalGachaItem"]
self.所有卡池 = self.限定十连 + self.联动十连 + self.普通十连
self.总掉落表 = []
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
if 分类类型 == "NONE":
匹配结果 = True
if 排序代码 <= 0:
匹配结果 = True
if 分类类型 == "NONE":
for item in self.总掉落表:
if 目标图标代码 == item["id"]:
匹配结果 = False
return 匹配结果
self.物品_名称_输出用 = {}
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 not 排除开关:
if os.path.exists(源文件路径):
目标文件路径 = f"./ui/public/depot/{中文名称}.webp"
if not os.path.exists(目标文件路径):
png_image = Image.open(源文件路径)
png_image.save(目标文件路径, "WEBP")
if 分类类型 != "NONE":
self.装仓库物品的字典[分类类型].append(
[目标文件路径, 源文件路径]
)
templist = [物品代码, 图标代码, 中文名称, 分类类型, 排序代码]
self.物品_名称_输出用[物品代码] = templist
self.物品_名称_模型用[中文名称] = templist
self.物品_名称_模型用[物品代码] = templist
print(f"复制 {源文件路径}{目标文件路径}")
with open("./mower/data/key_mapping.json", "w", encoding="utf8") as json_file:
json.dump(self.物品_名称_输出用, json_file, ensure_ascii=False, indent=4)
print()
def 干员列表(self):
"""读取干员列表并输出至./mower/data/agent.json"""
干员_名称列表 = {}
for 干员代码, 干员数据 in self.干员表.items():
if not 干员数据["itemObtainApproach"]:
continue
干员名 = 干员数据["name"]
干员_名称列表[干员名] = {
"key": 干员代码,
"profession": 干员数据["profession"],
}
干员头像路径 = f"./ArknightsGameResource/avatar/{干员代码}.png"
目标路径 = f"./ui/public/avatar/{干员数据['name']}.webp"
print(f"{干员名}: {干员代码}")
png_image = Image.open(干员头像路径)
png_image.save(目标路径, "WEBP")
干员_名称列表 = dict(sorted(干员_名称列表.items()))
with open("./mower/data/agent.json", "w", encoding="utf-8") as f:
json.dump(干员_名称列表, f, ensure_ascii=False, indent=4)
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):
"""读取活动关卡及掉落物并写入./ui/src/pages/stage_data/event_data.json"""
关卡 = 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"]
关卡消耗理智 = self.关卡表["stages"][]["apCost"]
# 关卡结束时间 = datetime.fromtimestamp(还未结束的非常驻关卡[键]["endTs"] + 1)
关卡掉落表 = self.关卡表["stages"][]["stageDropInfo"][
"displayDetailRewards"
]
self.总掉落表.extend(关卡掉落表)
关卡掉落 = {}
突袭首次掉落 = [
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=4)
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("./mower/data/recruit.json", "w", encoding="utf-8") as f:
json.dump(recruit_data, f, ensure_ascii=False, indent=4)
with open("./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("./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)
im = cv2.erode(im, np.ones((5, 5)))
template[operator] = im
with lzma.open("mower/models/recruit_result.pkl", "wb") as f:
pickle.dump(template, f)
def load_recruit_tag(self):
with open("./mower/data/recruit.json", "r", encoding="utf-8") as f:
recruit_agent = json.load(f)
font = ImageFont.truetype("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 sorted(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("./mower/models/recruit.pkl", "wb") as f:
pickle.dump(recruit_tag_template, f)
def 输出_公招资源(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):
"""输出扫描仓库的Knn模型
和添加物品至前端文件() 有联动 添加物品提供了分类的图片位置"""
self.训练仓库的knn模型("NORMAL", "./mower/models/NORMAL.pkl")
self.训练仓库的knn模型("CONSUME", "./mower/models/CONSUME.pkl")
self.训练仓库的knn模型("MATERIAL", "./mower/models/MATERIAL.pkl")
def 输出_在房间内的干员名的模型(self):
font = ImageFont.truetype("mower/fonts/SourceHanSansCN-Medium.otf", 37)
data = {}
kernel = np.ones((12, 12), np.uint8)
with open("./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("mower/models/operator_room.model", "wb") as f:
pickle.dump(data, f)
def avatar_and_portrait(self):
mapping = {} # char_285_medic2 -> Lancet-2
for name, data in self.干员表.items():
mapping[name] = data["name"]
for name in self.阿米娅表["infos"]["char_002_amiya"]["tmplIds"]:
mapping[name] = "阿米娅"
def extract(name, bg, crop):
data = {}
data_path = f"./ArknightsGameResource/{name}"
ORB = cv2.ORB_create(nfeatures=50, edgeThreshold=0, nlevels=1)
for i in os.listdir(data_path):
# i: char_285_medic2.png
for j, k in mapping.items():
# j: char_285_medic2
# k: Lancet-2
if i.startswith(j):
img = loadimg(os.path.join(data_path, i), True, bg)
if crop is not None:
img = cropimg(img, crop)
kp, des = ORB.detectAndCompute(img, None)
if k not in data:
data[k] = []
data[k].append(des)
break
with lzma.open(f"./mower/models/{name}.pkl", "wb") as f:
pickle.dump(data, f)
extract("avatar", "BLACK", None)
extract("portrait", (140, 140, 140, 255), ((0, 85), (180, 195)))
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=4)
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=4)
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} 已存在")
def 输出_前端基建资源(self):
self.获得干员基建描述()
self.转换输出基建buff()
self.添加基建技能图标()
def levels(self):
levels_json = 加载json("./ArknightsGameResource/levels.json")
with lzma.open("mower/models/levels.pkl", "wb") as f:
pickle.dump(levels_json, f)
def 读取模组(self):
_ = []
for item in self.模组["missionList"]:
_.append(self.模组["missionList"][item]["template"])
template_list = list({}.fromkeys(_).keys())
print(template_list)
roomType = {
"POWER": "发电站",
"DORMITORY": "宿舍",
"MANUFACTURE": "制造站",
"MEETING": "会客室",
"WORKSHOP": "加工站",
"TRADING": "贸易站",
"HIRE": "人力办公室",
"TRAINING": "训练室",
"CONTROL": "中枢",
}
if __name__ == "__main__":
数据处理器 = Arknights数据处理器()
数据处理器.干员列表()
数据处理器.读取卡池()
数据处理器.输出_活动关卡和掉落()
数据处理器.添加物品至前端文件() # 显示在仓库里的物品
数据处理器.输出_扫仓库模型()
数据处理器.输出_在房间内的干员名的模型()
数据处理器.avatar_and_portrait()
数据处理器.输出_前端基建资源()
数据处理器.输出_公招资源()
数据处理器.levels()
数据处理器.读取模组()

41
extract_restype.py Normal file
View file

@ -0,0 +1,41 @@
from pathlib import Path
res_path_name = "mower/resources/"
res_path = Path(res_path_name)
data = "from typing import Literal\n\nRes = Literal[\n"
references = {}
for i in sorted(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("mower/utils/typealias/res.py", "w", encoding="utf-8") as f:
f.write(data)
for py_file in sorted(Path("mower").glob("**/*.py")):
posix_path = py_file.as_posix()
if posix_path in [
"mower/utils/typealias/res.py",
"mower/utils/recognize/data.py",
]:
continue
with py_file.open("r", encoding="utf-8") as f:
content = f.read()
for name, matches in references.items():
if f'"{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}")

18
extract_scene.py Normal file
View file

@ -0,0 +1,18 @@
from 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("./mower/utils/scene.py", "w", encoding="utf-8") as f:
f.write(code)

BIN
logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
logo.png (Stored with Git LFS) Normal file

Binary file not shown.

72
manager.py Executable file
View file

@ -0,0 +1,72 @@
#!/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 sys
from pathlib import Path
from subprocess import Popen
Popen(
[sys.executable, "webview_ui.py", self.instances[idx]["path"]],
cwd=Path(__file__).parent,
)
def jump_to_index(window):
window.load_url("/manager/index.html")
if __name__ == "__main__":
api = Api()
window = webview.create_window(
title="多开管理器",
url="ui/dist/index.html",
js_api=api,
min_size=(400, 500),
width=400,
height=500,
)
webview.start(jump_to_index, window, http_server=True)

13
mower/__init__.py Normal file
View file

@ -0,0 +1,13 @@
import platform
from pathlib import Path
__version__ = "ng"
__rootdir__ = Path(__file__).parent.resolve()
from mower.utils.git_rev import revision_info
__version__ += "+" + revision_info()[:7]
__system__ = platform.system().lower()

421
mower/__main__.py Normal file
View file

@ -0,0 +1,421 @@
import json
from datetime import datetime, timedelta
from evalidate import Expr
from mower.solvers.base_schedule import BaseSchedulerSolver
from mower.solvers.depot_reader import DepotManager
from mower.solvers.reclamation_algorithm import ReclamationAlgorithm
from mower.solvers.secret_front import SecretFront
from mower.utils import config, path, rapidocr
from mower.utils.csleep import MowerExit
from mower.utils.datetime import format_time
from mower.utils.device.adb_client.session import Session
from mower.utils.device.scrcpy import Scrcpy
from mower.utils.email import send_message, task_template
from mower.utils.log import logger
from mower.utils.logic_expression import get_logic_exp
from mower.utils.path import get_path
from mower.utils.plan import Plan, PlanConfig, Room
from mower.utils.simulator import restart_simulator
base_scheduler = None
operators = config.operators
# 执行自动排班
def main():
logger.info("开始运行Mower")
rapidocr.initialize_ocr()
simulate()
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():
"""
具体调用方法可见各个函数的参数说明
"""
logger.info(f"正在使用全局配置空间: {path.global_space}")
tasks = []
reconnect_max_tries = 10
reconnect_tries = 0
global base_scheduler
success = False
while not success:
try:
base_scheduler = initialize(tasks)
success = True
except MowerExit:
return
except Exception as e:
logger.exception(e)
reconnect_tries += 1
if reconnect_tries < 3:
restart_simulator()
config.device.client.check_server_alive()
Session().connect()
if config.conf.droidcast.enable:
config.device.start_droidcast()
if config.conf.touch_method == "scrcpy":
config.device.control.scrcpy = Scrcpy(config.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 operators != {}:
for k, v in operators.items():
if (
k in base_scheduler.op_data.operators
and not base_scheduler.op_data.operators[k].room.startswith("dorm")
):
# 只复制心情数据
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
timezone_offset = 0
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
)
while True:
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 (
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():
depot_manager = DepotManager()
return depot_manager.读取仓库()[-1]
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:
config.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)
config.idle = True
base_scheduler.sleep(remaining_time)
config.idle = 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:
config.recog.update()
base_scheduler.back_to_index()
ra_solver = ReclamationAlgorithm()
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:
config.recog.update()
base_scheduler.back_to_index()
sf_solver = SecretFront()
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:
config.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)
config.idle = True
base_scheduler.sleep(remaining_time)
config.idle = False
base_scheduler.check_current_focus()
base_scheduler.run()
reconnect_tries = 0
except MowerExit:
return
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()
config.device.client.check_server_alive()
Session().connect()
if config.conf.droidcast.enable:
config.device.start_droidcast()
if config.conf.touch_method == "scrcpy":
config.device.control.scrcpy = Scrcpy(config.device.client)
continue
continue
else:
raise e
except RuntimeError as e:
logger.exception(f"程序出错-尝试重启模拟器->{e}")
restart_simulator()
config.device.client.check_server_alive()
Session().connect()
if config.conf.droidcast.enable:
config.device.start_droidcast()
if config.conf.touch_method == "scrcpy":
config.device.control.scrcpy = Scrcpy(config.device.client)
except Exception as e:
logger.exception(f"程序出错--->{e}")
config.recog.update()
def save_state(op_data, file="state.json"):
tmp_dir = get_path("@app/tmp")
if not tmp_dir.exists():
tmp_dir.mkdir()
state_file = tmp_dir / file
with open(state_file, "w") as f:
if op_data is not None:
json.dump(vars(op_data), f, default=str)
def load_state(file="state.json"):
state_file = get_path("@app/tmp") / file
if not state_file.exists():
return None
with open(state_file, "r") as f:
state = json.load(f)
operators = {k: Expr(v).eval() 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
logger.info("基建配置已加载!")
return operators

78
mower/data/__init__.py Normal file
View file

@ -0,0 +1,78 @@
import json
from pathlib import Path
from .. import __rootdir__
# agents list in Arknights
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"))
# the camps to which the clue belongs
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"))
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"))
# list of supported levels
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"))
# list of supported weekly levels
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"))
# recruit database
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")
)
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 = 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)

1382
mower/data/agent.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,166 @@
{
"令": [
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"
]
}

20
mower/data/base.json Normal file
View file

@ -0,0 +1,20 @@
[
"central",
"meeting",
"room_1_1",
"room_1_2",
"room_1_3",
"dormitory_1",
"factory",
"room_2_1",
"room_2_2",
"room_2_3",
"dormitory_2",
"contact",
"room_3_1",
"room_3_2",
"room_3_3",
"dormitory_3",
"train",
"dormitory_4"
]

5
mower/data/chapter.json Normal file
View file

@ -0,0 +1,5 @@
[
"HOUR OF AN AWAKENING",
"SHATTER OF A VISION",
"SHADOW OF A DYING SUN"
]

9
mower/data/clue.json Normal file
View file

@ -0,0 +1,9 @@
{
"莱茵生命": 1,
"企鹅物流": 2,
"黑钢": 3,
"乌萨斯学生自治团": 4,
"格拉斯哥帮": 5,
"喀兰贸易": 6,
"罗德岛制药": 7
}

5161
mower/data/key_mapping.json Normal file

File diff suppressed because it is too large Load diff

2114
mower/data/level.json Normal file

File diff suppressed because it is too large Load diff

1439
mower/data/recruit.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -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"
]
}

635
mower/data/scene.json Normal file
View file

@ -0,0 +1,635 @@
{
"-2": {
"label": "UNKNOWN_WITH_NAVBAR",
"comment": "有导航栏的未知场景"
},
"-1": {
"label": "UNKNOWN",
"comment": "未知"
},
"0": {
"label": "UNDEFINED",
"comment": "未定义"
},
"1": {
"label": "INDEX",
"comment": "首页"
},
"2": {
"label": "MATERIEL",
"comment": "物资领取确认"
},
"3": {
"label": "ANNOUNCEMENT",
"comment": "公告"
},
"4": {
"label": "MAIL",
"comment": "邮件信箱"
},
"5": {
"label": "NAVIGATION_BAR",
"comment": "导航栏返回"
},
"6": {
"label": "UPGRADE",
"comment": "升级"
},
"7": {
"label": "SKIP",
"comment": "开包动画"
},
"8": {
"label": "DOUBLE_CONFIRM",
"comment": "二次确认(未知)"
},
"9": {
"label": "CONNECTING",
"comment": "正在提交反馈至神经"
},
"10": {
"label": "NETWORK_CHECK",
"comment": "网络拨测"
},
"11": {
"label": "EXIT_GAME",
"comment": "退出游戏"
},
"12": {
"label": "DOWNLOAD_VOICE_RESOURCES",
"comment": "检测到有未下载的语音资源"
},
"13": {
"label": "AGREEMENT_UPDATE",
"comment": "协议更新"
},
"14": {
"label": "NOTICE",
"comment": "说明"
},
"15": {
"label": "INDEX_ORIGINITE",
"comment": "首页源石换玉"
},
"16": {
"label": "INDEX_SANITY",
"comment": "首页源石换理智"
},
"17": {
"label": "STORY",
"comment": "作战剧情"
},
"18": {
"label": "STORY_SKIP",
"comment": "作战剧情"
},
"101": {
"label": "LOGIN_MAIN",
"comment": "登录页面"
},
"102": {
"label": "LOGIN_INPUT",
"comment": "登录页面(输入)"
},
"103": {
"label": "LOGIN_QUICKLY",
"comment": "登录页面(快速)"
},
"104": {
"label": "LOGIN_LOADING",
"comment": "登录中"
},
"105": {
"label": "LOGIN_START",
"comment": "启动"
},
"106": {
"label": "LOGIN_ANNOUNCE",
"comment": "启动界面公告"
},
"107": {
"label": "LOGIN_REGISTER",
"comment": "注册"
},
"108": {
"label": "LOGIN_CAPTCHA",
"comment": "滑动验证码"
},
"109": {
"label": "LOGIN_BILIBILI",
"comment": "B 服登录界面"
},
"110": {
"label": "LOGIN_MAIN_NOENTRY",
"comment": "登录页面(无按钮入口)"
},
"111": {
"label": "LOGIN_CADPA_DETAIL",
"comment": "游戏适龄提示"
},
"112": {
"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": "基建全局视角"
},
"202": {
"label": "INFRA_TODOLIST",
"comment": "基建待办事项"
},
"203": {
"label": "INFRA_CONFIDENTIAL",
"comment": "线索主界面"
},
"204": {
"label": "INFRA_ARRANGE",
"comment": "基建干员进驻总览"
},
"205": {
"label": "INFRA_DETAILS",
"comment": "基建放大查看"
},
"206": {
"label": "INFRA_ARRANGE_CONFIRM",
"comment": "基建干员排班二次确认"
},
"207": {
"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": "离开基建"
},
"225": {
"label": "SANITY_CHARGE",
"comment": "急速充能"
},
"226": {
"label": "SANITY_CHARGE_DIALOG",
"comment": "急速充能对话框"
},
"227": {
"label": "CHOOSE_PRODUCT",
"comment": "制造站产物选择"
},
"228": {
"label": "SWITCH_ORDER",
"comment": "订单切换选择"
},
"229": {
"label": "PRODUCT_SWITCHING_CONFIRM",
"comment": "产物更改确认"
},
"301": {
"label": "BUSINESS_CARD",
"comment": "个人名片"
},
"302": {
"label": "FRIEND_LIST",
"comment": "好友列表"
},
"303": {
"label": "FRIEND_VISITING",
"comment": "基建内访问好友"
},
"304": {
"label": "BACK_TO_FRIEND_LIST",
"comment": "返回好友列表"
},
"401": {
"label": "MISSION_DAILY",
"comment": "日常任务"
},
"402": {
"label": "MISSION_WEEKLY",
"comment": "周常任务"
},
"403": {
"label": "MISSION_TRAINEE",
"comment": "见习任务"
},
"501": {
"label": "TERMINAL_MAIN",
"comment": "终端主界面"
},
"502": {
"label": "TERMINAL_MAIN_THEME",
"comment": "主题曲"
},
"503": {
"label": "TERMINAL_EPISODE",
"comment": "插曲"
},
"504": {
"label": "TERMINAL_BIOGRAPHY",
"comment": "别传"
},
"505": {
"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": "作战前,关卡已选定"
},
"603": {
"label": "OPERATOR_SELECT",
"comment": "作战前,正在编队"
},
"604": {
"label": "OPERATOR_ONGOING",
"comment": "代理作战"
},
"605": {
"label": "OPERATOR_FINISH",
"comment": "作战结束"
},
"607": {
"label": "OPERATOR_RECOVER_POTION",
"comment": "恢复理智(药剂)"
},
"608": {
"label": "OPERATOR_RECOVER_ORIGINITE",
"comment": "恢复理智(源石)"
},
"609": {
"label": "OPERATOR_DROP",
"comment": "掉落物品详细说明页"
},
"610": {
"label": "OPERATOR_ELIMINATE",
"comment": "剿灭作战前,关卡已选定"
},
"611": {
"label": "OPERATOR_ELIMINATE_FINISH",
"comment": "剿灭作战结束"
},
"612": {
"label": "OPERATOR_GIVEUP",
"comment": "放弃行动"
},
"613": {
"label": "OPERATOR_FAILED",
"comment": "代理作战失败"
},
"614": {
"label": "OPERATOR_ELIMINATE_AGENCY",
"comment": "剿灭代理卡使用确认"
},
"615": {
"label": "OPERATOR_SUPPORT",
"comment": "借助战"
},
"616": {
"label": "OPERATOR_SUPPORT_AGENT",
"comment": "使用助战干员界面"
},
"617": {
"label": "OPERATOR_AGENT_SELECT",
"comment": "作战干员选择界面"
},
"618": {
"label": "OPERATOR_FIGHT",
"comment": "作战中"
},
"701": {
"label": "SHOP_OTHERS",
"comment": "商店其它界面"
},
"702": {
"label": "SHOP_CREDIT",
"comment": "信用交易所"
},
"703": {
"label": "SHOP_CREDIT_CONFIRM",
"comment": "信用交易所兑换确认"
},
"704": {
"label": "SHOP_ASSIST",
"comment": "助战使用次数"
},
"705": {
"label": "SHOP_UNLOCK_SCHEDULE",
"comment": "累计信用消费"
},
"706": {
"label": "SHOP_TOKEN",
"comment": "凭证交易所"
},
"707": {
"label": "SHOP_TRADE_TOKEN",
"comment": "兑换溢出信物"
},
"801": {
"label": "RECRUIT_MAIN",
"comment": "公招主界面"
},
"802": {
"label": "RECRUIT_TAGS",
"comment": "挑选标签时"
},
"803": {
"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_EXIT_CONFIRM",
"comment": "结束当前保全作战"
},
"1009": {
"label": "SSS_TERMINATED",
"comment": "保全作战终止"
},
"1101": {
"label": "SF_ENTRANCE",
"comment": "隐秘战线入口"
},
"1102": {
"label": "SF_EXIT",
"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": "抵达终点"
},
"1201": {
"label": "HEADHUNTING",
"comment": "干员寻访"
},
"1301": {
"label": "DEPOT",
"comment": "仓库"
},
"1401": {
"label": "ACTIVITY_MAIN",
"comment": "活动主界面"
},
"1402": {
"label": "ACTIVITY_CHOOSE_LEVEL",
"comment": "活动关选择"
},
"1501": {
"label": "SIGN_IN_DAILY",
"comment": "签到活动"
},
"1502": {
"label": "MOON_FESTIVAL",
"comment": "月饼"
},
"9998": {
"label": "LOADING",
"comment": "场景跳转时的等待界面"
},
"9999": {
"label": "CONFIRM",
"comment": "确认对话框"
}
}

25
mower/data/shop.json Normal file
View file

@ -0,0 +1,25 @@
{
"招聘许可": "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"
}

170
mower/data/stage_data.json Normal file
View file

@ -0,0 +1,170 @@
[
{
"id": "",
"name": "上次作战",
"drop": "",
"end": -1,
"周一": 1,
"周二": 1,
"周三": 1,
"周四": 1,
"周五": 1,
"周六": 1,
"周日": 1,
"理智消耗":36
},
{
"id": "1-7",
"name": "1-7",
"drop": "",
"end": -1,
"周一": 1,
"周二": 1,
"周三": 1,
"周四": 1,
"周五": 1,
"周六": 1,
"周日": 1,
"理智消耗":6
},
{
"id": "Annihilation",
"name": "剿灭",
"drop": "",
"end": -1,
"周一": 1,
"周二": 1,
"周三": 1,
"周四": 1,
"周五": 1,
"周六": 1,
"周日": 1,
"理智消耗":25
},
{
"id": "LS-6",
"name": "经验书",
"drop": "",
"end": -1,
"周一": 1,
"周二": 1,
"周三": 1,
"周四": 1,
"周五": 1,
"周六": 1,
"周日": 1,
"理智消耗": 36
},
{
"id": "CE-6",
"name": "龙门币",
"drop": "",
"end": -1,
"周一": 0,
"周二": 1,
"周三": 0,
"周四": 1,
"周五": 0,
"周六": 1,
"周日": 1,
"理智消耗": 36
},
{
"id": "AP-5",
"name": "红票",
"drop": "",
"end": -1,
"周一": 1,
"周二": 0,
"周三": 0,
"周四": 1,
"周五": 0,
"周六": 1,
"周日": 1,
"理智消耗": 30
},
{
"id": "SK-5",
"name": "碳条",
"drop": "",
"end": -1,
"周一": 1,
"周二": 0,
"周三": 1,
"周四": 0,
"周五": 1,
"周六": 1,
"周日": 0,
"理智消耗": 30
},
{
"id": "CA-5",
"name": "技能书",
"drop": "",
"end": -1,
"周一": 0,
"周二": 1,
"周三": 1,
"周四": 0,
"周五": 1,
"周六": 0,
"周日": 1,
"理智消耗": 30
},
{
"id": "PR-A-2",
"name": "重装医疗2",
"drop": "",
"end": -1,
"周一": 1,
"周二": 0,
"周三": 0,
"周四": 1,
"周五": 1,
"周六": 0,
"周日": 1,
"理智消耗": 36
},
{
"id": "PR-B-2",
"name": "狙击术士2",
"drop": "",
"end": -1,
"周一": 1,
"周二": 1,
"周三": 0,
"周四": 0,
"周五": 1,
"周六": 1,
"周日": 0,
"理智消耗": 36
},
{
"id": "PR-C-2",
"name": "先锋辅助2",
"drop": "",
"end": -1,
"周一": 0,
"周二": 0,
"周三": 1,
"周四": 1,
"周五": 0,
"周六": 1,
"周日": 1,
"理智消耗": 36
},
{
"id": "PR-D-2",
"name": "近卫特种2",
"drop": "",
"end": -1,
"周一": 0,
"周二": 1,
"周三": 1,
"周四": 0,
"周五": 0,
"周六": 1,
"周日": 1,
"理智消耗": 36
}
]

11
mower/data/weekly.json Normal file
View file

@ -0,0 +1,11 @@
[
"固若金汤",
"摧枯拉朽",
"势不可挡",
"身先士卒",
"粉碎防御",
"空中威胁",
"战术演习",
"资源保障",
"货物运送"
]

200
mower/data/zone.json Normal file
View file

@ -0,0 +1,200 @@
{
"main_0": {
"type": "MAINLINE",
"name": "黑暗时代·上",
"chapterIndex": 0,
"zoneIndex": 0
},
"main_1": {
"type": "MAINLINE",
"name": "黑暗时代·下",
"chapterIndex": 0,
"zoneIndex": 1
},
"main_2": {
"type": "MAINLINE",
"name": "异卵同生",
"chapterIndex": 0,
"zoneIndex": 2
},
"main_3": {
"type": "MAINLINE",
"name": "二次呼吸",
"chapterIndex": 0,
"zoneIndex": 3
},
"main_4": {
"type": "MAINLINE",
"name": "急性衰竭",
"chapterIndex": 1,
"zoneIndex": 4
},
"main_5": {
"type": "MAINLINE",
"name": "靶向药物",
"chapterIndex": 1,
"zoneIndex": 5
},
"main_6": {
"type": "MAINLINE",
"name": "局部坏死",
"chapterIndex": 1,
"zoneIndex": 6
},
"main_7": {
"type": "MAINLINE",
"name": "苦难摇篮",
"chapterIndex": 1,
"zoneIndex": 7
},
"main_8": {
"type": "MAINLINE",
"name": "怒号光明",
"chapterIndex": 1,
"zoneIndex": 8
},
"main_9": {
"type": "MAINLINE",
"name": "风暴瞭望",
"chapterIndex": 2,
"zoneIndex": 9
},
"main_10": {
"type": "MAINLINE",
"name": "破碎日冕",
"chapterIndex": 2,
"zoneIndex": 10
},
"weekly_1": {
"type": "WEEKLY",
"name": "固若金汤",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_2": {
"type": "WEEKLY",
"name": "摧枯拉朽",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_3": {
"type": "WEEKLY",
"name": "势不可挡",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_4": {
"type": "WEEKLY",
"name": "身先士卒",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_5": {
"type": "WEEKLY",
"name": "粉碎防御",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_6": {
"type": "WEEKLY",
"name": "空中威胁",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_7": {
"type": "WEEKLY",
"name": "战术演习",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_8": {
"type": "WEEKLY",
"name": "资源保障",
"chapterIndex": null,
"zoneIndex": null
},
"weekly_9": {
"type": "WEEKLY",
"name": "货物运送",
"chapterIndex": null,
"zoneIndex": null
},
"permanent_sub_1_Darknights_Memoir": {
"type": "BRANCHLINE",
"name": "生于黑夜",
"chapterIndex": null,
"zoneIndex": 1
},
"permanent_sub_2_A_Walk_In_The_Dust": {
"type": "BRANCHLINE",
"name": "遗尘漫步",
"chapterIndex": null,
"zoneIndex": 2
},
"permanent_sub_3_Under_Tides": {
"type": "BRANCHLINE",
"name": "覆潮之下",
"chapterIndex": null,
"zoneIndex": 3
},
"permanent_sub_4_Stultifera_Navis": {
"type": "BRANCHLINE",
"name": "愚人号",
"chapterIndex": null,
"zoneIndex": 4
},
"permanent_sidestory_1_Grani_And_The_Treasure_Of_Knights": {
"type": "SIDESTORY",
"name": "骑兵与猎人",
"chapterIndex": null,
"zoneIndex": 1
},
"permanent_sidestory_2_Heart_Of_Surging_Flame": {
"type": "SIDESTORY",
"name": "火蓝之心",
"chapterIndex": null,
"zoneIndex": 2
},
"permanent_sidestory_3_Code_Of_Brawl": {
"type": "SIDESTORY",
"name": "喧闹法则",
"chapterIndex": null,
"zoneIndex": 3
},
"permanent_sidestory_4_Twilight_Of_Wolumonde": {
"type": "SIDESTORY",
"name": "沃伦姆德的薄暮",
"chapterIndex": null,
"zoneIndex": 4
},
"permanent_sidestory_5_The_Great_Chief_Returns": {
"type": "SIDESTORY",
"name": "密林悍将归来",
"chapterIndex": null,
"zoneIndex": 5
},
"permanent_sidestory_6_Maria_Nearl": {
"type": "SIDESTORY",
"name": "玛莉娅·临光",
"chapterIndex": null,
"zoneIndex": 6
},
"permanent_sidestory_7_Mansfield_Break": {
"type": "SIDESTORY",
"name": "孤岛风云",
"chapterIndex": null,
"zoneIndex": 7
},
"permanent_sidestory_8_Who_is_Real": {
"type": "SIDESTORY",
"name": "画中人",
"chapterIndex": null,
"zoneIndex": 8
},
"permanent_sidestory_9_Dossoles_Holiday": {
"type": "SIDESTORY",
"name": "多索雷斯假日",
"chapterIndex": null,
"zoneIndex": 9
}
}

3
mower/fonts/README.md Normal file
View file

@ -0,0 +1,3 @@
# Fonts
精简过的字体文件,包含所有干员名称内出现过的的汉字

BIN
mower/fonts/SourceHanSansCN-Medium.otf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/CONSUME.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/MATERIAL.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/NORMAL.pkl (Stored with Git LFS) Normal file

Binary file not shown.

16
mower/models/README.md Normal file
View file

@ -0,0 +1,16 @@
# Models
## dbnet.onnx
DBNET 的模型文件,负责提取图像中的文字
## crnn_lite_lstm.onnx
CRNN 的轻量型模型文件,负责识别图像中的文字
## svm.model
SVM 分类器的模型文件,负责图像匹配判定
## depot.pkl
仓库物品的knn模型,负责仓库中的物品

25
mower/models/__init__.py Normal file
View file

@ -0,0 +1,25 @@
import lzma
import pickle
from mower import __rootdir__
with lzma.open(f"{__rootdir__}/models/avatar.pkl", "rb") as f:
avatar = pickle.load(f)
with lzma.open(f"{__rootdir__}/models/portrait.pkl", "rb") as f:
portrait = 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)

BIN
mower/models/avatar.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/levels.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/navigation.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/noto_sans.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/operator_room.model (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/portrait.pkl (Stored with Git LFS) Normal file

Binary file not shown.

View file

BIN
mower/models/recruit.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/recruit_result.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/riic_base_digits.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/secret_front.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/shop.pkl (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/models/svm.model (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/12cadpa.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/1800.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,3 @@
# Resources
资源文件,用于识别游戏中的元素和场景判定

BIN
mower/resources/all_in.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/announcement_close.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_blue_yes.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_check_in.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_check_in_on.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_confirm.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_order_options.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/arrange_order_options_scene.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/bill_accelerate.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/biography.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/business_card.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/battle_confirm.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/battle_empty.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/clear.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/clear_battle.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/confirm.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/empty_skill_slot.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/fast_select.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/foldup.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/open_profession.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/perfer.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/perfer_agent.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/perfer_choosed.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/ALL.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/CASTER.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/MEDIC.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/PIONEER.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/SNIPER.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/SPECIAL.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/SUPPORT.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/TANK.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/WARRIOR.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/choose_arrow.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/profession/skill.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/rect.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/riic_empty.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/support_skill_be_choosen.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/support_status.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_agent/trigger.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/choose_product_options.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue/1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue/2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue/3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue/4.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
mower/resources/clue/5.png (Stored with Git LFS) Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more