Skip to content
Open
7 changes: 7 additions & 0 deletions assets/game_data/screen_info/challenge_mission.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ area_list:
template_match_threshold: 0.7
color_range: null
goto_list: []
- area_name: 预计消耗体力-其他
id_mark: false
pc_rect:
- 1214
- 958
- 1500
- 1014
- area_name: 次数加-其他
id_mark: false
pc_rect:
Expand Down
17 changes: 17 additions & 0 deletions assets/game_data/screen_info/ornamenet_extraction.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ area_list:
template_sub_dir: ''
template_id: ''
template_match_threshold: 0.7
- area_name: 按钮-预设编队
pc_rect:
- 365
- 60
- 500
- 100
text: 预设编队
lcs_percent: 0.55
template_sub_dir: ''
template_id: ''
template_match_threshold: 0.7
- area_name: 区域-预设编队名称
pc_rect:
- 30
- 100
- 300
- 1000
- area_name: 按钮-支援
pc_rect:
- 672
Expand Down
8 changes: 5 additions & 3 deletions src/sr_od/app/div_uni/operations/choose_oe_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def check_screen(self) -> OperationRoundResult:
识别当前所在的画面
:return:
"""
if self.num < 1 or self.num > 4:
return self.round_fail('存档编号只能是1~4')
if self.num > 4:
return self.round_fail('存档编号只能是0~4, 0代表使用默认档案')

if self.num == 0:
return self.round_success('使用默认档案')
Expand Down Expand Up @@ -91,7 +91,9 @@ def choose_file(self) -> OperationRoundResult:

result = self.round_by_find_area(screen, '饰品提取', '按钮-存档使用中')
if result.is_success:
self.round_by_click_area('菜单', '')
result = self.round_by_click_area('菜单', '右上角返回')
if not result.is_success:
return result
return self.round_success(result.status, wait=1.5) # 已经在使用了 返回即可

return self.round_retry(result.status, wait=1)
17 changes: 3 additions & 14 deletions src/sr_od/app/div_uni/operations/choose_oe_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,16 @@ def __init__(self, ctx: SrContext, character_id: str | None):
self.found_character: bool = False
"""是否找到支援角色"""

@operation_node(name='识别画面', is_start_node=True)
def check_screen(self) -> OperationRoundResult:
@operation_node(name='点击支援按钮', is_start_node=True)
def click_support(self) -> OperationRoundResult:
"""
识别画面
点击支援按钮
:return:
"""
if self.character_id is None:
return self.round_success('无需支援')

screen = self.last_screenshot

return self.round_by_find_area(screen, '饰品提取', '左上角标题-饰品提取', retry_wait=1)

@node_from(from_name='识别画面', status='左上角标题-饰品提取')
@operation_node(name='点击支援按钮')
def click_support(self) -> OperationRoundResult:
"""
点击支援按钮
:return:
"""
screen = self.last_screenshot
return self.round_by_find_and_click_area(screen, '饰品提取', '按钮-支援',
success_wait=1, retry_wait=1)

Expand Down
60 changes: 51 additions & 9 deletions src/sr_od/app/div_uni/operations/ornamenet_extraction.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from typing import Optional, Callable

from one_dragon.base.geometry.point import Point
from one_dragon.base.matcher.match_result import MatchResultList
from one_dragon.base.operation.operation_edge import node_from
from one_dragon.base.operation.operation_node import operation_node
from one_dragon.base.operation.operation_round_result import OperationRoundResult
from one_dragon.utils import str_utils, cv2_utils
from one_dragon.utils.i18_utils import gt
from one_dragon.utils.log_utils import log
from sr_od.app.div_uni.operations.choose_oe_file import ChooseOeFile
Expand All @@ -21,7 +24,7 @@
class ChallengeOrnamentExtraction(SrOperation):

def __init__(self, ctx: SrContext, mission: GuideMission, run_times: int,
diff: int, file_num: int, support_character: str,
diff: int, file_num: int, team_name: str, support_character: str,
Comment thread
JoshCai233 marked this conversation as resolved.
get_reward_callback: Optional[Callable[[int], None]] = None):
SrOperation.__init__(self, ctx, op_name=gt('饰品提取', 'game'))

Expand All @@ -34,6 +37,9 @@ def __init__(self, ctx: SrContext, mission: GuideMission, run_times: int,
self.file_num: int = file_num
"""需要使用的存档 0为不选择"""

self.team_name: str = team_name
"""预设编队名称"""

self.diff: int = diff
"""需要挑战的难度 0为不选择"""

Expand Down Expand Up @@ -61,7 +67,7 @@ def handle_init(self) -> Optional[OperationRoundResult]:

return None

@operation_node(name='传送', is_start_node=True)
@operation_node(name='传送', is_start_node=True, screenshot_before_round=False)
def tp(self) -> OperationRoundResult:
"""
TODO 未加入难度选择
Expand Down Expand Up @@ -98,14 +104,50 @@ def tp(self) -> OperationRoundResult:
#
# return self.round_success()

# todo 这个函数是不是没用
# @node_from(from_name='传送')
# @operation_node(name='选择存档')
# def choose_file(self) -> OperationRoundResult:
# op = ChooseOeFile(self.ctx, self.file_num)
# return self.round_by_op_result(op.execute())

@node_from(from_name='传送')
@operation_node(name='选择存档', screenshot_before_round=False)
def choose_file(self) -> OperationRoundResult:
op = ChooseOeFile(self.ctx, self.file_num)
return self.round_by_op_result(op.execute())

@node_from(from_name='选择存档')
@operation_node(name='点击预设编队按钮', screenshot_before_round=False)
def click_predefined_team(self) -> OperationRoundResult:
if len(self.team_name) == 0:
return self.round_success('使用默认编队')
result = self.round_by_click_area('饰品提取', '支援入队踢4号位角色')
if result.is_success:
return self.round_by_find_and_click_area(self.screenshot(), '饰品提取', '按钮-预设编队',
success_wait=1, retry_wait=1)
return result

@node_from(from_name='点击预设编队按钮')
@operation_node(name='选择预设编队')
def choose_predefined_team(self) -> OperationRoundResult:
area = self.ctx.screen_loader.get_area('饰品提取', '区域-预设编队名称')
part = cv2_utils.crop_image_only(self.last_screenshot, area.rect)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ocr_result_map = self.ctx.ocr.run_ocr(part)

name_list = []
mrl_list: list[MatchResultList] = []
for ocr_word, mrl in ocr_result_map.items():
name_list.append(ocr_word)
mrl_list.append(mrl)

# 找队伍
team_idx = str_utils.find_best_match_by_difflib(gt(self.team_name), name_list, cutoff=0.5)
if team_idx is not None:
self.ctx.controller.click(area.left_top + mrl_list[team_idx][0].center)
return self.round_success(wait=0.5)

# 没找到, 向上滑动翻页
drag_start = Point(area.rect.x1 + 100, area.rect.y2 - 100)
drag_end = Point(area.rect.x1 + 100, area.rect.y1 + 100)
self.ctx.controller.drag_to(start=drag_start, end=drag_end)
return self.round_retry('未找到编队: ' + self.team_name, wait=1)

@node_from(from_name='点击预设编队按钮', status='使用默认编队')
@node_from(from_name='选择预设编队')
@operation_node(name='选择支援')
def choose_support(self) -> OperationRoundResult:
op = ChooseOeSupport(self.ctx, self.support_character)
Expand Down
6 changes: 4 additions & 2 deletions src/sr_od/app/trailblaze_power/trailblaze_power_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def check_task(self) -> OperationRoundResult:
return self.round_success(status=TrailblazePowerApp.STATUS_WITH_PLAN)

@node_from(from_name='检查当前需要挑战的关卡', status=STATUS_WITH_PLAN)
@node_from(from_name='执行开拓力计划')
@operation_node(name='打开指南检查体力')
def open_guide(self) -> OperationRoundResult:
op = GuideCheckPower(self.ctx)
Expand All @@ -63,7 +64,6 @@ def open_guide(self) -> OperationRoundResult:
return self.round_by_op_result(op_result)

@node_from(from_name='打开指南检查体力')
@node_from(from_name='执行开拓力计划')
@operation_node(name='执行开拓力计划')
def execute_plan(self) -> OperationRoundResult:
self.ctx.power_config.check_plan_run_times()
Expand All @@ -86,6 +86,7 @@ def execute_plan(self) -> OperationRoundResult:
if plan is None:
break

# noinspection PyUnboundLocalVariable
if can_run_times == 0:
return self.round_success(TrailblazePowerApp.STATUS_NO_ENOUGH_POWER)

Expand Down Expand Up @@ -122,7 +123,8 @@ def execute_plan(self) -> OperationRoundResult:
op = ChallengeOrnamentExtraction(self.ctx, mission,
run_times=run_times,
diff=0,
file_num=plan.team_num,
file_num=plan.file_num,
team_name=plan.team_name,
support_character=plan.support if plan.support != 'none' else None,
get_reward_callback=self.on_oe_get_reward)
else:
Expand Down
12 changes: 11 additions & 1 deletion src/sr_od/app/trailblaze_power/trailblaze_power_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
class TrailblazePowerPlanItem:

def __init__(self, mission_id: str, team_num: int, support: str, plan_times: int,
run_times: int = 0, diff: int = 0):
run_times: int = 0, diff: int = 0, file_num: int = 0, team_name: str = ''):
self.mission_id: str = mission_id # 关卡id - 新
self.team_num: int = team_num # 使用配队 0代表游戏内当前配队
self.file_num: int = file_num # 饰品提取 存档编号 0代表游戏内当前编号
self.team_name: str = team_name # 用于饰品提取选择配队
self.support: str = support # 支援角色 'none'或者None就是没有
self.plan_times: int = plan_times # 计划通关次数
self.run_times: int = run_times # 已经通关次数
Expand Down Expand Up @@ -61,6 +63,8 @@ def add_plan(self) -> None:
item = TrailblazePowerPlanItem(
mission.unique_id,
team_num=0 if history is None else history.team_num,
file_num=0 if history is None else history.file_num,
team_name='' if history is None else history.team_name,
support='none' if history is None else history.support,
plan_times=1 if history is None else history.plan_times,
run_times=0,
Expand Down Expand Up @@ -135,6 +139,8 @@ def save(self) -> None:
{
'mission_id': i.mission_id,
'team_num': i.team_num,
'file_num': i.file_num,
'team_name': i.team_name,
'support': i.support,
'plan_times': i.plan_times,
'run_times': i.run_times,
Expand All @@ -152,6 +158,8 @@ def save(self) -> None:
continue

history_data['team_num'] = i.team_num
history_data['file_num'] = i.file_num
history_data['team_name'] = i.team_name
history_data['support'] = i.support
history_data['plan_times'] = i.plan_times
history_data['diff'] = i.diff
Expand All @@ -162,6 +170,8 @@ def save(self) -> None:
history_teams.append({
'mission_id': i.mission_id,
'team_num': i.team_num,
'file_num': i.file_num,
'team_name': i.team_name,
'support': i.support,
'plan_times': i.plan_times,
'diff': i.diff
Expand Down
48 changes: 39 additions & 9 deletions src/sr_od/challenge_mission/use_trailblaze_power.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import math

from PIL.ImageChops import screen
from typing import Optional, Callable, ClassVar

from one_dragon.base.operation.operation_edge import node_from
from one_dragon.base.operation.operation_node import operation_node
from one_dragon.base.operation.operation_round_result import OperationRoundResult
from one_dragon.utils import str_utils
from one_dragon.utils.i18_utils import gt
from one_dragon.utils.log_utils import log
from sr_od.challenge_mission.choose_challenge_times import ChooseChallengeTimes
Expand Down Expand Up @@ -54,6 +57,9 @@ def __init__(self, ctx: SrContext, mission: GuideMission,
self.current_challenge_times: int = 1 # 当前挑战的次数
self.finish_times: int = 0 # 已经完成的次数
self.battle_fail_times: int = 0 # 战斗失败次数
# 培养目标识别单次体力和关卡连续运行上限
self.mission_power_ocr: int = 0
self.mission_challenge_times: int = 6

@node_from(from_name='阵亡传送恢复')
@operation_node(name='传送', is_start_node=True)
Expand Down Expand Up @@ -90,8 +96,31 @@ def _get_current_challenge_times(self) -> int:
return min(24, current_challenge_times)
elif self.mission.cate.cn == '凝滞虚影':
return min(8, current_challenge_times)
elif self.mission.cate.cn in ['侵蚀隧洞', '培养目标']:
elif self.mission.cate.cn in ['侵蚀隧洞']:
return min(6, current_challenge_times)
elif self.mission.cate.cn == '培养目标':
if self.mission_power_ocr == 0:
# 识别单次体力消耗
area1 = self.ctx.screen_loader.get_area('挑战副本', '预计消耗体力-其他')
ocr_result_list = self.ctx.ocr_service.get_ocr_result_list(self.last_screenshot, rect=area1.rect)
Comment thread
JoshCai233 marked this conversation as resolved.
if len(ocr_result_list) > 0:
power_unit_str = ocr_result_list[0].data
if power_unit_str[0] in ['×', 'x']:
power_unit_str = power_unit_str[1:]
Comment on lines +108 to +109
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

power_unit_str 为空时 power_unit_str[0] 会抛出 IndexError

OCR 结果 .data 可能为空字符串,此时直接索引 [0] 崩溃。同时 Ruff (RUF001) 警告 × 是 Unicode 乘号,建议显式标注以避免视觉歧义。

🐛 建议修复
-                    if power_unit_str[0] in ['×', 'x']:
-                        power_unit_str = power_unit_str[1:]
+                    if power_unit_str and power_unit_str[0] in ['\u00d7', 'x']:  # × 或 x 前缀
+                        power_unit_str = power_unit_str[1:]
🧰 Tools
🪛 Ruff (0.15.2)

[warning] 108-108: String contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?

(RUF001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sr_od/challenge_mission/use_trailblaze_power.py` around lines 108 - 109,
Guard against empty OCR output before indexing by checking power_unit_str is
truthy (and/or len(power_unit_str) > 0) and then test its first character;
replace the current condition with something like: if power_unit_str and
power_unit_str[0] in ['\u00D7', 'x', 'X'] (use the explicit Unicode multiply
sign '\u00D7' instead of a plain '×' to avoid visual ambiguity) and then strip
the leading unit; reference the power_unit_str variable in this file
(use_trailblaze_power.py) when applying the change.

mission_power_ocr = str_utils.get_positive_digits(power_unit_str, err=None)
if mission_power_ocr is None:
log.error('识别体力单位失败:' + power_unit_str)
elif self.mission.power < mission_power_ocr:
log.error('哪个本体力消耗>40? 识别的体力为: ' + power_unit_str)
return 1
else:
self.mission_power_ocr = mission_power_ocr
# 根据单次体力消耗计算真实一次可以打的次数 (替换掉默认的40体力一次)
self.mission_challenge_times = math.floor(self.mission_challenge_times * self.mission.power / self.mission_power_ocr)
self.plan_times = math.floor(self.plan_times * self.mission.power / self.mission_power_ocr)
current_challenge_times = math.floor(current_challenge_times * self.mission.power / self.mission_power_ocr)
Comment thread
JoshCai233 marked this conversation as resolved.
self.mission.power = self.mission_power_ocr
return min(self.mission_challenge_times, current_challenge_times)
return 1

@node_from(from_name='选择次数')
Expand All @@ -110,16 +139,17 @@ def confirm_after_click_challenge(self) -> OperationRoundResult:
screen = self.last_screenshot

result1 = self.round_by_find_area(screen, '挑战副本', '开拓力弹框-标题')
if not result1.is_success:
# 培养目标-周本 挑战次数用完
result1 = self.round_by_find_area(screen, '挑战副本', '提示弹框-次数用完')
if result1.is_success:
if self.on_battle_success is not None:
self.on_battle_success(0, 200) # 清空开拓力
return self.round_by_find_and_click_area(screen, '挑战副本', '开拓力弹框-取消',
success_wait=1, retry_wait=1)
return self.round_by_find_and_click_area(screen, '挑战副本', '开拓力弹框-取消', success_wait=1, retry_wait=1)
Comment thread
JoshCai233 marked this conversation as resolved.

result2 = self.round_by_find_area(screen, '挑战副本', '阵亡弹框-标题')
if result2.is_success:
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消',
retry_wait=1)
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消', retry_wait=1)

return self.round_retry('无对话框', wait=0.3)

Expand Down Expand Up @@ -158,8 +188,7 @@ def _after_start_challenge(self) -> OperationRoundResult:
# 有阵亡角色
result2 = self.round_by_find_area(screen, '挑战副本', '阵亡弹框-标题')
if result2.is_success:
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消',
success_wait=1, retry_wait=1)
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消', success_wait=1, retry_wait=1)


if self.mission.cate.cn == '凝滞虚影':
Expand Down Expand Up @@ -235,8 +264,7 @@ def confirm_after_challenge_again(self) -> OperationRoundResult:

result2 = self.round_by_find_area(screen, '挑战副本', '阵亡弹框-标题')
if result2.is_success:
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消',
retry_wait=1)
return self.round_by_find_and_click_area(screen, '挑战副本', '阵亡弹框-取消', retry_wait=1)

return self.round_retry('无对话框', wait=0.5)

Expand Down Expand Up @@ -269,6 +297,8 @@ def __debug_op():
tab = ctx.guide_data.best_match_tab_by_name('生存索引')
category = ctx.guide_data.best_match_category_by_name('拟造花萼(赤)', tab)
mission = ctx.guide_data.best_match_mission_by_name('存护之蕾', category, '克劳克影视乐园')
# category = ctx.guide_data.best_match_category_by_name('培养目标', tab)
# mission = ctx.guide_data.best_match_mission_by_name('培养目标', category, None)

op = UseTrailblazePower(ctx, mission, 2, 2, support='hyacine')

Expand Down
9 changes: 8 additions & 1 deletion src/sr_od/config/team_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class TeamNumEnum(Enum):

DEFAULT = ConfigItem('默认配队', 0)
TEAM_1 = ConfigItem('编队1', 1)
TEAM_2 = ConfigItem('编队2', 2)
Expand All @@ -15,3 +14,11 @@ class TeamNumEnum(Enum):
TEAM_7 = ConfigItem('编队7', 7)
TEAM_8 = ConfigItem('编队8', 8)
TEAM_9 = ConfigItem('编队9', 9)


class FileNumEnum(Enum):
DEFAULT = ConfigItem('默认存档', 0)
TEAM_1 = ConfigItem('存档1', 1)
TEAM_2 = ConfigItem('存档2', 2)
TEAM_3 = ConfigItem('存档3', 3)
TEAM_4 = ConfigItem('存档4', 4)
Loading