Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main beta #767

Merged
merged 75 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
c2d7864
fix: 修复部分情形下副词条未满4个的遗器识别失败
weiduhuo Nov 10, 2023
5c69155
fix: 再次修正由于背景光点导致的人物名称识别错误
weiduhuo Nov 10, 2023
466ac6d
chore: 使配装与队伍的选项按名称排序
weiduhuo Nov 10, 2023
c2f5470
chore: 更改‘relics_loadout.json’数据格式,为未来版本预留键位,并兼容旧版本格式
weiduhuo Nov 10, 2023
5e0190a
chore: 将select置于questionary命名下
weiduhuo Nov 10, 2023
e48831e
fix: 在识别角色名称前正确切换窗口
weiduhuo Nov 10, 2023
6011a09
chore: 取消自动矫正角色列表,改为用户手动,以支持混沌队伍
weiduhuo Nov 10, 2023
ae3fc2f
fix: ‘search_relic’函数的超时检测未正常生效
weiduhuo Nov 10, 2023
494aea9
chore: 为‘search_relic’函数增加搜索数量限制
weiduhuo Nov 10, 2023
18fceca
perf: 在筛选遗器前尝试匹配当前遗器,加快配装装备
weiduhuo Nov 10, 2023
b6480f7
fix: 处理遗器搜索时遗器列表为空的情形
weiduhuo Nov 10, 2023
b7656a3
chore: 增加模块初始化时对队伍数据完整性的检查
weiduhuo Nov 10, 2023
449b945
feat: 为questionary提供小数校验与命名冲突校验函数
weiduhuo Nov 10, 2023
002b02c
chore: 将‘questionary.text’方法替换‘input’
weiduhuo Nov 10, 2023
a17f6f7
chore: 添加'速度%'属性
weiduhuo Nov 10, 2023
9570f08
chore: 添加额外属性,并构成全属性名称
weiduhuo Nov 10, 2023
b1563aa
chore: 添加遗器套装效果数据
weiduhuo Nov 10, 2023
b152953
feat: 为‘questionary.Choice.description’添加格式化文本功能
weiduhuo Nov 10, 2023
9561e87
merge branch 'add-questionary'
weiduhuo Nov 10, 2023
48af8d6
feat: 新增风格化文本的append与print功能
weiduhuo Nov 10, 2023
0d3c24c
chore: 使‘Array2dict’兼容数组与序列类型
weiduhuo Nov 10, 2023
753fc13
chore: 使‘get_loadout_brief’可选择返回套装计数器
weiduhuo Nov 10, 2023
739cc4d
chore: 定义StatsEffect类型
weiduhuo Nov 14, 2023
f5d22c0
feat: 打印配装详情时显示遗器套装效果
weiduhuo Nov 14, 2023
9e3f4ee
chore: 更改FloatValidator的警告说明
weiduhuo Nov 14, 2023
17b67ab
chore: 完善Array2dict,当key为空时返回None
weiduhuo Nov 14, 2023
c87cf58
merge branch 'feature-relic-set-effect'
weiduhuo Nov 14, 2023
37e133e
fix: 'questionary.select'的'disable'选项可通过快捷键交互
weiduhuo Nov 14, 2023
370e8ab
fix: 'questionary.select'计算选项数量时包含了'Separator'
weiduhuo Nov 14, 2023
8d15a02
chore: 优化属性名称的定义顺序
weiduhuo Nov 14, 2023
bec588c
merge branch 'add-questionary'
weiduhuo Nov 14, 2023
d87fb34
chore: 更改额外属性名称
weiduhuo Nov 14, 2023
3524bbf
chore: 改用'questionary.confirm'
weiduhuo Nov 14, 2023
e1fc10b
chore: 将'questionary.select.use_jk_keys'的缺损值改为False
weiduhuo Nov 17, 2023
af5dab0
Merge branch 'add-questionary' into feature-relic
weiduhuo Nov 17, 2023
b1f2078
feat: 完善风格化文本,新增extend,splitlines,combine功能
weiduhuo Nov 17, 2023
d0f9164
fix: 修复'防御力'属性部分情况OCR识别
weiduhuo Nov 17, 2023
b89a0b7
chore: 更改遗器模块相关json数据的存储位置,并兼容旧版本
weiduhuo Nov 17, 2023
d7e0dad
fix: 补充‘relics_loadout.json’数据格式的更改缺漏
weiduhuo Nov 17, 2023
4900080
fix: 修复搜索遗器时跳行功能失效导致死循环
weiduhuo Nov 17, 2023
1e52270
feat: 添加角色裸装面板
weiduhuo Nov 17, 2023
f25c822
feat: 添加角色属性权重
weiduhuo Nov 17, 2023
127a93f
feat: 添加副词条挡位权重选择
weiduhuo Nov 17, 2023
d6affc6
chore: 添加对风格化文本的支持
weiduhuo Nov 17, 2023
9d12719
refactor: 重构'print_relic'方法,支持输出风格化文本
weiduhuo Nov 17, 2023
217b30f
chore: 添加遗器模块用户数据
weiduhuo Nov 18, 2023
5e72c93
chore: 优化遗器模块的数据文件布局
weiduhuo Nov 18, 2023
2d8d85b
refactor: 重构打印配装面板详情的方法
weiduhuo Nov 18, 2023
5bcdd53
feat: 新增打印配装六件遗器详情的方法
weiduhuo Nov 18, 2023
4c535d8
chore: 设置通过开关切换打印面板详情与遗器详情的功能
weiduhuo Nov 18, 2023
c5c91b2
chore: 整合并封装角色配装选择的方法
weiduhuo Nov 18, 2023
11a17c4
chore: 优化多处选择与打印功能
weiduhuo Nov 18, 2023
065ada3
chore: 当'search_relic'未激活匹配状态时,可返回所遍历的遗器数据
weiduhuo Nov 18, 2023
a98aaa7
perf: 提高遗器搜索的性能
weiduhuo Nov 18, 2023
5417818
feat: 添加编辑角色裸装面板的方法
weiduhuo Nov 18, 2023
95dc225
feat: 添加编辑角色属性权重的方法
weiduhuo Nov 18, 2023
6ad830d
chore: 分解队伍配装校验函数
weiduhuo Nov 18, 2023
d0abf30
feat: 添加修改配装数据的功能
weiduhuo Nov 18, 2023
5c0c91d
feat: 添加编辑角色配装的方法
weiduhuo Nov 18, 2023
ccc950f
feat: 添加批量识别遗器的方法
weiduhuo Nov 18, 2023
b36c5ae
chore: 修改遗器模块的入口函数
weiduhuo Nov 18, 2023
e24e087
chore: 添加游戏1.5版本的遗器数据
weiduhuo Nov 18, 2023
3f302a7
为风格化文本的combine方法添加前缀功能
weiduhuo Nov 19, 2023
05aa72c
feat: 添加角色属性权重的打印功能
weiduhuo Nov 19, 2023
c24e3c5
fix: 修复多处由于数据未充分拷贝造成的错误
weiduhuo Nov 19, 2023
7136c12
chore: 匹配游戏1.4版本的遗器套装筛选界面,并做优化
weiduhuo Nov 19, 2023
8c6a69d
chore: 使'StatsWeight'适配权重打印
weiduhuo Nov 19, 2023
4b8c60f
test: 完成模拟器端的功能测试,修复若干bug,并作优化
weiduhuo Nov 19, 2023
8625c84
merge branch 'main-beta'
weiduhuo Nov 19, 2023
c1538f7
test: 完成PC端功能测试,并做优化
weiduhuo Nov 19, 2023
4c3259a
docs: 更新遗器模块说明
weiduhuo Nov 19, 2023
975f3ba
fix: 更正错别字
weiduhuo Nov 20, 2023
043b468
fix: 修复不足4个副词条的遗器联合打印时发生错位
weiduhuo Nov 20, 2023
ac77828
chore: 添加遗器识别失败时的提示,并作截图保存
weiduhuo Nov 20, 2023
9cf5540
Merge pull request #765 from weiduhuo/feature-relic
Night-stars-1 Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ LICENSE
picture_list.json
version.json
*relics*.json
char_panel.json
char_weight.json
screencast.png
screencast1.png
utils/A_.py
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,20 @@ This software is open source, free of charge and for learning and exchange purpo
- [ ] 模拟宇宙正在开发
- [x] GUI开发
- [ ] 后续将会新增找宝箱、锄大地顺带捡垃圾等功能
- [x] 遗器模块 (不支持GUI)
- [x] 遗器的识别、匹配、搜索
- [x] 角色配装的保存、编辑、读取并装备
- [x] 队伍配装的保存、读取并装备
- [x] 支持创建、编辑、载入角色裸装面板与属性权重
- [x] 支持对相关文本的风格化打印
- [ ] 对遗器与角色配装的评估、推荐

[项目进度请点击查看](https://github.com/users/Night-stars-1/projects/2)

## 遗器模块展示
<img src="https://github.com/weiduhuo/StarRailAssistant/blob/picture/relic1.gif" />
<img src="https://github.com/weiduhuo/StarRailAssistant/blob/picture/relic2.gif" />

## 贡献

[问题反馈](https://github.com/Starry-Wind/StarRailAssistant/issues/new/choose) | [PR 提交](https://github.com/Starry-Wind/StarRailAssistant/compare)
Expand Down
3 changes: 3 additions & 0 deletions data/fixed_data/char_weight_default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"说明": "此文件为预留文件,待后续开发 (内容物为角色的默认属性权重)"
}
148 changes: 142 additions & 6 deletions utils/calculated.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import sys
import time
import copy
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
Expand All @@ -22,6 +23,8 @@
from pynput.keyboard import Controller as KeyboardController
from pynput.keyboard import Key
from pynput.mouse import Controller as MouseController
from questionary import Validator, ValidationError, Style
from collections.abc import Iterable

from .config import CONFIG_FILE_NAME, _, get_file, sra_config_obj
from .cv_tools import CV_Tools, show_img
Expand Down Expand Up @@ -699,6 +702,7 @@ def part_ocr(self, points = (0,0,0,0), debug=False, left=False, number=False, im
if debug:
log.info(data)
# show_img(img_fp)
os.makedirs("logs/image", exist_ok=True)
timestamp_str = str(int(datetime.timestamp(datetime.now())))
cv.imwrite(f"logs/image/relic_{str(points)}_{timestamp_str}.png", img_fp)
else:
Expand Down Expand Up @@ -993,16 +997,19 @@ def change_team(self):

class Array2dict:

def __init__(self, arr: np.ndarray, key_index: int = -1, value_index: Optional[int]=None):
def __init__(self, arr: Union[np.ndarray, List[str]], key_index: int = -1, value_index: Optional[int]=None):
"""
说明:
将np数组转化为字典暂住内存,用于对数组短时间内的频繁查找
将np数组或序列转化为字典暂住内存,用于短时间内的频繁查找
参数:
:param arr: 二维数组
:param arr: 二维数组或一维序列
:param key_index: 待查找的关键字所在的数组列标
:param value_index: 待查找的数值所在的数组列标 (为空时表示查找关键字的行标)
"""
if arr.ndim != 2:
if isinstance(arr, list):
self.data_dict = {element: idx for idx, element in enumerate(arr)}
return
if isinstance(arr, np.ndarray) and arr.ndim != 2:
raise ValueError("输入的数组必须为二维数组")
# 将np数组转化为字典
if value_index is None: # 默认将key的行标作为value,以取代np.where
Expand All @@ -1012,8 +1019,137 @@ def __init__(self, arr: np.ndarray, key_index: int = -1, value_index: Optional[i
# log.debug(self.data_dict)

def __getitem__(self, key: Any) -> Any:
return self.data_dict[key]
return self.data_dict.get(key, None)


class FloatValidator(Validator):
"""
说明:
为questionary校验所输入的文本是否为规定范围内的小数
"""
def __init__(self, st: Optional[float]=None, ed: Optional[float]=None) -> None:
super().__init__()
self.st = st
self.ed = ed

def validate(self, document):
try:
number = float(document.text)
except ValueError:
raise ValidationError(message=_("请输入整数或小数"))
if self.st is not None and number < self.st:
raise ValidationError(message=_("数字小于下界{}").format(self.st))
if self.ed is not None and number > self.ed:
raise ValidationError(message=_("数字大于下界{}").format(self.ed))


class ConflictValidator(Validator):
"""
说明:
为questionary校验所输入的文本是否存在命名冲突
"""
def __init__(self, names: Iterable[str]) -> None:
super().__init__()
self.names = names

def validate(self, document):
if document.text in self.names:
raise ValidationError(message=_("存在命名冲突"), cursor_position=len(document.text))


class StyledText(List[Tuple[str, str]]):
"""
说明:
风格化文本序列,继承 List[Tuple[str, str]],(0-style_class, 1-text),
重载了`append()`和`extend()`方法,支持链式操作。
可直接作为`questionary.Choice.description`的初始化参数
"""
def __getitem__(self, key: slice) -> List["StyledText"]:
return StyledText(super().__getitem__(key))

def append(self, text: Union[str, Tuple[str, str]], style_class: str="") -> "StyledText":
if isinstance(text, str):
if style_class and "class:" not in style_class:
style_class = "class:" + style_class
super().append((style_class, text))
else:
super().append(text)
return self

def extend(self, *texts: Iterable[Tuple[str, str]], sep: Optional[Union[str, Tuple[str, str]]]=None, indent: int=0) -> "StyledText":
"""
说明:
继承`list.extend()`
参数:
:param texts: 任意个`StyledText`类型的文本序列
:param sep: 插入在文本序列间的内容,默认为空
:param indent: 起始位置的缩进长度,默认为零
"""
if indent:
self.append(" "*indent)
for i, __iterable in enumerate(texts):
if i and sep:
self.append(sep)
super().extend(__iterable)
return self

def splitlines(self, keepends=False) -> List["StyledText"]:
"""
说明:
按行分割为`StyledText`序列
"""
lines_list = [StyledText()]
def end_with_n(s :str) -> bool:
return s[-1] == "\n"
for style, text in self:
if text == "":
continue
lines = str(text).splitlines(keepends=True)
lines_list[-1].append(
(style, lines[0][:-1] if end_with_n(lines[0]) and not keepends else lines[0])
)
for line in lines[1:]:
lines_list.append(
StyledText([
(style, line[:-1] if end_with_n(line) and not keepends else line)
]))
if end_with_n(lines[-1]): # 开启空的一行
lines_list.append(StyledText())
if not lines_list[-1]: # 删除无效行
lines_list.pop()
return lines_list

def combine_styled_text(*texts: StyledText, prefix: Optional[Union[str, Tuple[str, str]]]=None, **kwargs) -> StyledText:
"""
说明:
将多个风格化文本序列,按行进行横向并联
参数:
:param sep: 插入在文本序列间的内容,默认为空
:param prefix: 文本前缀
:param indent: 起始位置的缩进长度,默认为零
"""
result = StyledText()
lines_list_list: List[List[StyledText]] = []
if prefix:
result.append(prefix)
for text in texts:
lines_list_list.append(text.splitlines())
for line in zip(*lines_list_list):
result.extend(*line, **kwargs)
result.append("\n")
return result

def print_styled_text(text: StyledText, style: Style, **kwargs: Any) -> None:
"""
说明:
打印风格化文本
"""
from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import FormattedText

print_formatted_text(FormattedText(text), style=style, **kwargs)


def get_data_hash(data: Any, key_filter: Optional[List[str]]=None, speed_modified=False) -> str:
"""
说明:
Expand All @@ -1027,7 +1163,7 @@ def get_data_hash(data: Any, key_filter: Optional[List[str]]=None, speed_modifie
if not key_filter:
tmp_data = data
elif isinstance(data, dict):
tmp_data = {key: value for key, value in data.items() if key not in key_filter}
tmp_data = copy.deepcopy({key: value for key, value in data.items() if key not in key_filter}) # 深拷贝
if speed_modified and _("速度") in tmp_data["subs_stats"]:
tmp_data["subs_stats"][_("速度")] = float(int(tmp_data["subs_stats"][_("速度")])) # 去除小数部分
else:
Expand Down
37 changes: 28 additions & 9 deletions utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,43 @@
from .log import log

CONFIG_FILE_NAME = "config.json"
RELIC_FILE_NAME = "relics_set.json"
LOADOUT_FILE_NAME = "relics_loadout.json"
TEAM_FILE_NAME = "relics_team.json"

USER_DATA_PREFIX = "data/user_data/"
FIXED_DATA_PREFIX = "data/fixed_data/"
os.makedirs(USER_DATA_PREFIX, exist_ok=True)

RELIC_FILE_NAME = USER_DATA_PREFIX + "relics_set.json"
LOADOUT_FILE_NAME = USER_DATA_PREFIX + "relics_loadout.json"
TEAM_FILE_NAME = USER_DATA_PREFIX + "relics_team.json"
CHAR_PANEL_FILE_NAME = USER_DATA_PREFIX + "char_panel.json"
CHAR_WEIGHT_FILE_NAME = USER_DATA_PREFIX + "char_weight.json"


def normalize_file_path(filename):
# 尝试在当前目录下读取文件
current_dir = os.getcwd()
file_path = os.path.join(current_dir, filename)
if os.path.exists(file_path):
return file_path
pre_file_path = os.path.join(current_dir, filename)
if os.path.exists(pre_file_path):
return pre_file_path
else:
# 如果当前目录下没有该文件,则尝试在上一级目录中查找
parent_dir = os.path.dirname(current_dir)
file_path = os.path.join(parent_dir, filename)
if os.path.exists(file_path):
return file_path
else:
# 如果上一级目录中也没有该文件,则返回None
return None
# 如果仍然没有,则尝试在当前目录仅查找文件名
pre_filename = str(filename).rsplit('/', 1)[-1]
file_path = os.path.join(current_dir, pre_filename)
if os.path.exists(file_path):
if str(filename).rsplit('/', 1)[0] == USER_DATA_PREFIX[:-1]:
# 判断为旧版本 (<=1.8.7) 数据文件位置
import shutil
shutil.move(file_path, pre_file_path)
log.info(_("文件位置更改,由'{}'迁移至'{}'").format(pre_filename, filename))
return pre_file_path
return file_path
# 如果仍然没有,则返回None
return None


def read_json_file(filename: str, path=False, schema:dict=None) -> dict:
Expand Down Expand Up @@ -328,6 +345,8 @@ class SRAData(metaclass=SRADataMeta):
"""是否在打印遗器信息时显示拓展信息"""
ndigits_for_relic: int = 2
"""在打印遗器信息时的小数精度"""
stats_weight_for_relic: int = 0
"""遗器副词条档位权重:0-空,1-主流赋值,2-真实比例赋值,3-主流赋值比例矫正"""
auto_shutdown: bool = False
"""是否自动关机"""

Expand Down
10 changes: 7 additions & 3 deletions utils/questionary/questionary/prompts/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class Choice:
shortcut_key: Optional[str]
"""A shortcut key for the choice"""

description: Optional[str]
description: Optional[FormattedText]
"""Choice description"""

def __init__(
Expand All @@ -82,7 +82,7 @@ def __init__(
disabled: Optional[str] = None,
checked: Optional[bool] = False,
shortcut_key: Optional[Union[str, bool]] = True,
description: Optional[str] = None,
description: Optional[FormattedText] = None,
) -> None:
self.disabled = disabled
self.title = title
Expand Down Expand Up @@ -445,7 +445,11 @@ def append(index: int, choice: Choice):
description = current.description

if description is not None:
tokens.append(("class:text", " Description: {}".format(description)))
tokens.append(("class:text", " Description: "))
if isinstance(description, list):
tokens.extend(description)
else:
tokens.append(("class:text", description))
else:
tokens.pop() # Remove last newline.
return tokens
Expand Down
22 changes: 13 additions & 9 deletions utils/questionary/questionary/prompts/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def select(
use_shortcuts: bool = False,
use_arrow_keys: bool = True,
use_indicator: bool = False,
use_jk_keys: bool = True,
use_jk_keys: bool = False,
use_emacs_keys: bool = True,
show_selected: bool = False,
show_description: bool = True,
Expand Down Expand Up @@ -135,14 +135,18 @@ def select(
if choices is None or len(choices) == 0:
raise ValueError("A list of choices needs to be provided.")

if use_shortcuts and len(choices) > len(InquirerControl.SHORTCUT_KEYS):
raise ValueError(
"A list with shortcuts supports a maximum of {} "
"choices as this is the maximum number "
"of keyboard shortcuts that are available. You"
"provided {} choices!"
"".format(len(InquirerControl.SHORTCUT_KEYS), len(choices))
if use_shortcuts:
real_len_of_choices = sum(
1 for c in choices if not isinstance(c, Separator)
)
if real_len_of_choices > len(InquirerControl.SHORTCUT_KEYS):
raise ValueError(
"A list with shortcuts supports a maximum of {} "
"choices as this is the maximum number "
"of keyboard shortcuts that are available. You "
"provided {} choices!"
"".format(len(InquirerControl.SHORTCUT_KEYS), real_len_of_choices)
)

merged_style = merge_styles_default([style])

Expand Down Expand Up @@ -204,7 +208,7 @@ def _(event):
"for movement are disabled. "
"This choice is not reachable.".format(c.title)
)
if isinstance(c, Separator) or c.shortcut_key is None:
if isinstance(c, Separator) or c.shortcut_key is None or c.disabled:
continue

# noinspection PyShadowingNames
Expand Down
Loading
Loading