Skip to content

返回主界面的界面识别中 添加 差分宇宙4.0#586

Open
JoshCai233 wants to merge 2 commits intoOneDragon-Anything:mainfrom
JoshCai233:返回主界面-差分宇宙4.0

Hidden character warning

The head ref may contain hidden characters: "\u8fd4\u56de\u4e3b\u754c\u9762-\u5dee\u5206\u5b87\u5b994.0"
Open

返回主界面的界面识别中 添加 差分宇宙4.0#586
JoshCai233 wants to merge 2 commits intoOneDragon-Anything:mainfrom
JoshCai233:返回主界面-差分宇宙4.0

Conversation

@JoshCai233
Copy link
Copy Markdown
Contributor

@JoshCai233 JoshCai233 commented Apr 20, 2026

Summary by CodeRabbit

发布说明

  • 新功能

    • 增加了对差分宇宙4.0新内容的支持
    • 新增游戏屏幕状态识别,包括站点选择、愿力满盈等交互界面
  • 优化

    • 调整了部分UI交互区域的位置坐标以提升准确性

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

概览

本变更为模拟宇宙系统增加了"差分宇宙4.0"的支持,包括新增四个屏幕状态(选择站点卡、选择下一站、愿力满盈、欢愉假面),更新了屏幕坐标检测逻辑,扩展了枚举成员,并调整了退出流程。

变更内容

队列 / 文件 摘要
屏幕数据配置
assets/game_data/screen_info/sim_uni.yml
更新了"大世界返回按钮"和"楼层类型"区域的PC坐标定位,新增"差分宇宙-暂离"区域条目,配置其矩形范围和交互属性。
常量与枚举定义
src/sr_od/app/sim_uni/sim_uni_const.py
SimUniLevelTypeEnum中新增ANY成员,扩展了关卡类型的识别范围。
屏幕状态检测
src/sr_od/app/sim_uni/sim_uni_screen_state.py
ScreenState枚举新增四个状态值用于差分宇宙4.0场景识别;改进了get_level_type的匹配逻辑,采用子串包含判断替代模糊匹配;扩展了get_sim_uni_screen_state的检测流程,添加了早期差分宇宙4.0状态识别块。
退出操作逻辑
src/sr_od/app/sim_uni/operations/sim_uni_exit.py
click_exit方法中新增早期暂离区域点击尝试,允许在标准退出流程前执行该操作。
世界返回流程
src/sr_od/operations/back_to_normal_world_plus.py
添加了对新增差分宇宙4.0屏幕状态的处理逻辑,通过坐标点击和延时操作完成相应交互;重构了check_screen方法的控制流;新增调试入口函数。

预估代码审查工作量

🎯 3 (中等) | ⏱️ ~20 分钟

诗歌

🐰 新宇宙来临,站点闪烁迎,
愿力满盈时,假面舞翩翩,
屏幕识界新,坐标已更正,
四个新状态,流程更通顺!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 标题准确总结了主要变更:在返回主界面的界面识别中添加对差分宇宙4.0的支持,与所有修改文件的核心目标相符。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/sr_od/operations/back_to_normal_world_plus.py (1)

72-111: 考虑将差分宇宙4.0的选择操作沉淀为 screen_info 配置。

当前使用的 Point(...) 坐标会通过 game2win_pos() 自动适配窗口和分辨率,不会因此失效。但这些硬编码坐标对应的按钮(站点卡、下一站、奇迹、面具)在 sim_uni.yml 中尚无配置,建议补充相关区域定义并使用 round_by_click_area 替换,便于游戏更新时维护。

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

In `@src/sr_od/operations/back_to_normal_world_plus.py` around lines 72 - 111, The
hardcoded Point(...) clicks in back_to_normal_world_plus.py for
sim_uni_screen_state.ScreenState.SELECT_STATION, SELECT_NEXT_STATION,
CHOOSE_WILL_POWER and AHA_MASK should be replaced with area-based clicks backed
by sim_uni.yml; add appropriate area definitions (e.g., select_station_area,
select_next_station_area, choose_will_power_area, aha_mask_area) to sim_uni.yml
and update the handlers in the method (currently using
self.ctx.controller.click(Point(...)) and then return self.round_wait(...)) to
call the reusable round_by_click_area helper (or equivalent area-click wrapper)
with those area names and preserve the existing short sleeps and round_wait
calls so behavior and timing remain intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/sr_od/app/sim_uni/operations/sim_uni_exit.py`:
- Around line 73-75: The new attempt to click '差分宇宙-暂离' inside SimUniExit via
round_by_find_and_click_area prematurely returns and skips the settlement flow
(click_confirm()/click_empty()), breaking cases that must finish and claim
rewards; remove or relocate that '差分宇宙-暂离' branch from SimUniExit so SimUniExit
always proceeds through the confirmation and settlement steps (ensure
click_confirm() and click_empty() are executed), and instead implement the
"temporarily leave" behavior in a dedicated fallback like BackToNormalWorldPlus
or add an explicit parameter/mode to SimUniExit (e.g., exit_mode or
leave_vs_settle) so callers such as SimUniRunEliteRouteV2._boss_exit() can
choose the settle path.

In `@src/sr_od/app/sim_uni/sim_uni_const.py`:
- Around line 122-123: The new ANY SimUniLevelType ('ANY') causes a runtime
AttributeError because match_next_level_entry() iterates SimUniLevelTypeEnum and
calls get_template('sim_uni', 'level_type_any') which returns None and then
accesses template.features; update match_next_level_entry() to explicitly skip
the ANY enum (check for the SimUniLevelType name or value, e.g., 'any') before
calling get_template(), or alternatively add the missing template
assets/template/sim_uni/level_type_any/raw.png so get_template(...) returns a
valid template; ensure the fix prevents accessing template.features when
get_template returns None.

In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py`:
- Around line 124-132: The LCS matcher find_best_match_by_lcs returns an index
(0 is a valid match) so the current if checks treat 0 as False and miss matches
at titles[0]; update each conditional that calls find_best_match_by_lcs (for
ScreenState.SELECT_STATION.value, SELECT_NEXT_STATION.value,
CHOOSE_WILL_POWER.value, AHA_MASK.value) to check explicitly for None (e.g.,
result is not None) or compare against None, ensuring a returned 0 is treated as
a successful match and the corresponding ScreenState is returned.
- Around line 52-57: get_level_type currently only uses strict substring
matching (targets = [i for i, w in enumerate(target_list) if w in region_name])
which returns None on small OCR errors; change it to first attempt the strict
substring check and if no targets are found, fall back to the original
fuzzy/approximate matching logic to pick the best candidate. Specifically, keep
the existing substring pass using target_list, region_name and level_type_list;
if that yields no result, run the prior fuzzy matching (e.g.,
difflib.SequenceMatcher or the previous similarity function) across target_list
vs region_name to pick the highest-scoring index above a sensible threshold
(e.g., 0.6) and return level_type_list for that index, otherwise return None.
Ensure you update get_level_type and reuse the same variables (targets,
target_list, region_name, level_type_list) so callers are unaffected.

---

Nitpick comments:
In `@src/sr_od/operations/back_to_normal_world_plus.py`:
- Around line 72-111: The hardcoded Point(...) clicks in
back_to_normal_world_plus.py for
sim_uni_screen_state.ScreenState.SELECT_STATION, SELECT_NEXT_STATION,
CHOOSE_WILL_POWER and AHA_MASK should be replaced with area-based clicks backed
by sim_uni.yml; add appropriate area definitions (e.g., select_station_area,
select_next_station_area, choose_will_power_area, aha_mask_area) to sim_uni.yml
and update the handlers in the method (currently using
self.ctx.controller.click(Point(...)) and then return self.round_wait(...)) to
call the reusable round_by_click_area helper (or equivalent area-click wrapper)
with those area names and preserve the existing short sleeps and round_wait
calls so behavior and timing remain intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a64c10d-336a-4195-bde8-4ee8b65dca81

📥 Commits

Reviewing files that changed from the base of the PR and between ba00fff and 323d949.

📒 Files selected for processing (5)
  • assets/game_data/screen_info/sim_uni.yml
  • src/sr_od/app/sim_uni/operations/sim_uni_exit.py
  • src/sr_od/app/sim_uni/sim_uni_const.py
  • src/sr_od/app/sim_uni/sim_uni_screen_state.py
  • src/sr_od/operations/back_to_normal_world_plus.py

Comment on lines +73 to +75
result = self.round_by_find_and_click_area(screen, '模拟宇宙', '差分宇宙-暂离')
if result.is_success:
return self.round_success(result.status)
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 期望:如果 SimUniExit 被非“返回主界面”路径调用,需要保留结束结算语义,不应默认点击“暂离”。

rg -n -C4 "SimUniExit\\(|sim_uni_exit\\("

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 5403


🏁 Script executed:

cat -n src/sr_od/app/sim_uni/operations/sim_uni_exit.py

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 5108


🏁 Script executed:

# Check the context of each SimUniExit call to understand their requirements
echo "=== BackToNormalWorldPlus usage ==="
sed -n '30,50p' src/sr_od/operations/back_to_normal_world_plus.py

echo ""
echo "=== SimUniApp usage ==="
sed -n '165,175p' src/sr_od/app/sim_uni/sim_uni_app.py

echo ""
echo "=== SimUniRunEliteRouteV2 usage ==="
sed -n '220,230p' src/sr_od/app/sim_uni/operations/move_v2/sim_uni_run_elite_route_v2.py

echo ""
echo "=== SimUniRunWorld usage ==="
sed -n '65,73p' src/sr_od/app/sim_uni/operations/auto_run/sim_uni_run_world.py

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 2012


🏁 Script executed:

# Check if there are any specific comments or documentation about "暂离" (temporary leave)
rg -n "暂离" src/sr_od/app/sim_uni/operations/sim_uni_exit.py -B2 -A2

# Also check what UI areas are defined for "暂离"
rg -n "差分宇宙-暂离" --type yaml --type json --type txt

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 360


不应在"结束并结算"操作中优先尝试"暂离"。

SimUniExit 的明确语义是"结束并结算"。新增的"差分宇宙-暂离"点击(第73-75行)如果成功会直接返回,绕过后续的确认对话框(click_confirm())和结算画面(click_empty()),导致跳过整个结算流程。这在需要真正结算获得奖励的场景(如 SimUniRunEliteRouteV2._boss_exit() 战胜首领后)会造成功能破坏。

建议:将"暂离"逻辑移至 BackToNormalWorldPlus 等专门处理返回的兜底操作,或为 SimUniExit 增加显式参数区分"暂离"和"结算"两种模式。

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

In `@src/sr_od/app/sim_uni/operations/sim_uni_exit.py` around lines 73 - 75, The
new attempt to click '差分宇宙-暂离' inside SimUniExit via
round_by_find_and_click_area prematurely returns and skips the settlement flow
(click_confirm()/click_empty()), breaking cases that must finish and claim
rewards; remove or relocate that '差分宇宙-暂离' branch from SimUniExit so SimUniExit
always proceeds through the confirmation and settlement steps (ensure
click_confirm() and click_empty() are executed), and instead implement the
"temporarily leave" behavior in a dedicated fallback like BackToNormalWorldPlus
or add an explicit parameter/mode to SimUniExit (e.g., exit_mode or
leave_vs_settle) so callers such as SimUniRunEliteRouteV2._boss_exit() can
choose the settle path.

Comment on lines +122 to +123
# 在差分宇宙4.0中的识别标志
ANY = SimUniLevelType('any', '位面')
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 期望:能找到 level_type_any 模板,或能看到 match_next_level_entry 对 ANY 的显式跳过逻辑。

fd -i 'level_type_any'
rg -n -C4 "get_template\\('sim_uni', level_type\\.template_id\\)|SimUniLevelTypeEnum\\.ANY|type_id == 'any'|level_type\\.type_id == 'any'"

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 880


🏁 Script executed:

# 查看 SimUniLevelType 类定义,确认 template_id 如何生成
rg -n "class SimUniLevelType|def template_id|self.template_id" src/sr_od/app/sim_uni/sim_uni_const.py -A 3

# 查看 match_next_level_entry 的完整实现,包括异常处理
rg -n "def match_next_level_entry" src/sr_od/app/sim_uni/ -A 30

# 查看模板目录中存在的 level_type 模板
find . -type f -name "level_type_*" 2>/dev/null | head -20

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 3124


🏁 Script executed:

# 查看 template_loader.get_template 的实现
rg -n "def get_template|raise|except|None" src/sr_od/ -B 2 -A 10 | grep -A 15 "def get_template"

# 查看 match_next_level_entry 是否被 try-except 包裹
rg -n "match_next_level_entry" src/sr_od/ -B 5 -A 35 | grep -E "(try|except|raise)"

# 查看实际存在的 level_type_* 模板文件
find . -path "*/sim_uni*" -type f -name "level_type_*" 2>/dev/null
find . -type d -name "sim_uni" 2>/dev/null

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 4205


🏁 Script executed:

# 列出 sim_uni 模板目录中的所有文件
find ./assets/template/sim_uni -type f 2>/dev/null | sort

# 查看 template_loader.get_template 的实现
rg -n "def get_template" src/ -A 15

# 检查 ANY 枚举在其他文件中的使用情况
rg "SimUniLevelTypeEnum.ANY|type_id == 'any'|ANY" src/sr_od/app/sim_uni/ -n

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 21976


🏁 Script executed:

# 查看 load_template 的完整实现
rg -n "def load_template" src/one_dragon/base/screen/template_loader.py -A 20

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 862


🏁 Script executed:

# 查看 get_template 的完整实现
rg -n "def get_template" src/one_dragon/base/screen/template_loader.py -A 12 | head -30

# 查看 is_template_existed 的实现
rg -n "def is_template_existed" src/one_dragon/base/screen/ -A 5

Repository: OneDragon-Anything/StarRailOneDragon

Length of output: 1464


新增的 ANY 枚举会导致运行时崩溃,因为缺失对应的 level_type_any 模板。

match_next_level_entry() 中,代码遍历所有 SimUniLevelTypeEnum 并尝试加载对应的模板。当迭代到新增的 ANY 枚举时,会调用 get_template('sim_uni', 'level_type_any'),但该模板不存在。此时 get_template() 返回 None,后续访问 template.features 会触发 AttributeError

需要以下其中一种方案:

  • match_next_level_entry() 中跳过 ANY 枚举,或
  • level_type_any 添加对应的模板文件(assets/template/sim_uni/level_type_any/raw.png
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sr_od/app/sim_uni/sim_uni_const.py` around lines 122 - 123, The new ANY
SimUniLevelType ('ANY') causes a runtime AttributeError because
match_next_level_entry() iterates SimUniLevelTypeEnum and calls
get_template('sim_uni', 'level_type_any') which returns None and then accesses
template.features; update match_next_level_entry() to explicitly skip the ANY
enum (check for the SimUniLevelType name or value, e.g., 'any') before calling
get_template(), or alternatively add the missing template
assets/template/sim_uni/level_type_any/raw.png so get_template(...) returns a
valid template; ensure the fix prevents accessing template.features when
get_template returns None.

Comment on lines +52 to +57
targets = [i for i, w in enumerate(target_list) if w in region_name]

if target_idx is None or target_idx < 0:
if len(targets) == 0:
return None
else:
return level_type_list[target_idx]
return level_type_list[targets[0]]
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 | 🟠 Major

保留 OCR 模糊匹配兜底。

这里从模糊匹配改成严格子串包含后,只要 OCR 出现空格、漏字或错字,get_level_type() 就会返回 None,下游返回大世界逻辑会误判不在模拟宇宙。建议先做子串命中,再 fallback 到原来的近似匹配。

建议调整
-    targets = [i for i, w in enumerate(target_list) if w in region_name]
-
-    if len(targets) == 0:
-        return None
-    else:
-        return level_type_list[targets[0]]
+    targets = [i for i, w in enumerate(target_list) if w in region_name]
+    if len(targets) > 0:
+        return level_type_list[targets[0]]
+
+    idx = str_utils.find_best_match_by_difflib(region_name, target_list)
+    if idx is None:
+        return None
+    return level_type_list[idx]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py` around lines 52 - 57,
get_level_type currently only uses strict substring matching (targets = [i for
i, w in enumerate(target_list) if w in region_name]) which returns None on small
OCR errors; change it to first attempt the strict substring check and if no
targets are found, fall back to the original fuzzy/approximate matching logic to
pick the best candidate. Specifically, keep the existing substring pass using
target_list, region_name and level_type_list; if that yields no result, run the
prior fuzzy matching (e.g., difflib.SequenceMatcher or the previous similarity
function) across target_list vs region_name to pick the highest-scoring index
above a sensible threshold (e.g., 0.6) and return level_type_list for that
index, otherwise return None. Ensure you update get_level_type and reuse the
same variables (targets, target_list, region_name, level_type_list) so callers
are unaffected.

Comment on lines +124 to +132
# region 差分宇宙4.0
if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1):
return ScreenState.SELECT_STATION.value
if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1):
return ScreenState.SELECT_NEXT_STATION.value
if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7):
return ScreenState.CHOOSE_WILL_POWER.value
if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7):
return ScreenState.AHA_MASK.value
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

修正 LCS 返回下标为 0 时识别失败的问题。

find_best_match_by_lcs() 返回的是下标;当匹配结果是 0 时,当前 if 判断会走 False,导致这些新状态在标题列表首位时无法识别。这里应统一使用 is not None

建议调整
-    if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1):
+    if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1) is not None:
         return ScreenState.SELECT_STATION.value
-    if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1):
+    if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1) is not None:
         return ScreenState.SELECT_NEXT_STATION.value
-    if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7):
+    if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7) is not None:
         return ScreenState.CHOOSE_WILL_POWER.value
-    if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7):
+    if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7) is not None:
         return ScreenState.AHA_MASK.value
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py` around lines 124 - 132, The
LCS matcher find_best_match_by_lcs returns an index (0 is a valid match) so the
current if checks treat 0 as False and miss matches at titles[0]; update each
conditional that calls find_best_match_by_lcs (for
ScreenState.SELECT_STATION.value, SELECT_NEXT_STATION.value,
CHOOSE_WILL_POWER.value, AHA_MASK.value) to check explicitly for None (e.g.,
result is not None) or compare against None, ensuring a returned 0 is treated as
a successful match and the corresponding ScreenState is returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant