feat(combat): add customizable fixed team axes - #210
Conversation
📝 WalkthroughWalkthrough本 PR 新增了完整的"团队轴(Team Axis)"框架:定义了 Changes团队轴(Team Axis)系统全量引入
Sequence Diagram(s)sequenceDiagram
participant AutoCombatTask
participant CustomCharManager
participant TeamAxisRegistry
participant BaseTeamAxis
rect rgba(70, 130, 180, 0.5)
note over AutoCombatTask,CustomCharManager: 战斗开始,首次创建固定轴
AutoCombatTask->>CustomCharManager: get_fixed_team()
AutoCombatTask->>CustomCharManager: get_fixed_team_axis()
AutoCombatTask->>TeamAxisRegistry: create_matching_team_axis(task, axis_id)
TeamAxisRegistry->>BaseTeamAxis: matches(task.chars)
TeamAxisRegistry-->>AutoCombatTask: team_axis 实例 or None
end
rect rgba(60, 179, 113, 0.5)
note over AutoCombatTask,BaseTeamAxis: 战斗循环
loop 每轮循环
alt team_axis 存在
AutoCombatTask->>BaseTeamAxis: perform_next()
BaseTeamAxis->>BaseTeamAxis: run_opening() / run_cycle()
else 原逻辑
AutoCombatTask->>AutoCombatTask: get_current_char().perform()
end
end
AutoCombatTask->>BaseTeamAxis: on_combat_end()
end
sequenceDiagram
participant TeamAxisTab
participant CustomCharManager
participant CustomTeamAxis
participant TeamAxisRegistry
TeamAxisTab->>CustomCharManager: get_fixed_team()
TeamAxisTab->>TeamAxisRegistry: get_axis_classes()
TeamAxisTab->>TeamAxisTab: fixed_team_signature()
TeamAxisTab->>CustomTeamAxis: validate_axis_syntax(content)
CustomTeamAxis-->>TeamAxisTab: (valid, error)
TeamAxisTab->>CustomCharManager: set_custom_team_axis(axis_id, name, ...)
TeamAxisTab->>CustomCharManager: set_fixed_team_axis(enabled, axis_id)
TeamAxisTab->>TeamAxisTab: refresh_state()
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
tests/TestTeamAxis.py (1)
299-302: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win时间补丁建议改为固定返回值,降低实现耦合导致的测试脆弱性。
Line 299-302 与 Line 354-357 目前用
side_effect=(100.0, 100.0),如果实现内部新增一次monotonic()调用会直接触发StopIteration。这里更适合return_value=100.0。建议修改
`@patch`( "src.team_axis.axes.NanallyZeroJiuyuanHotoriAxis.time.monotonic", - side_effect=(100.0, 100.0), + return_value=100.0, ) ... `@patch`( "src.team_axis.axes.NanallyZeroJiuyuanHotoriAxis.time.monotonic", - side_effect=(100.0, 100.0), + return_value=100.0, )Also applies to: 354-357
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/TestTeamAxis.py` around lines 299 - 302, The patch decorator for `src.team_axis.axes.NanallyZeroJiuyuanHotoriAxis.time.monotonic` uses `side_effect=(100.0, 100.0)` which restricts the mock to return a value exactly twice, causing StopIteration if the implementation makes additional calls to monotonic(). Replace `side_effect=(100.0, 100.0)` with `return_value=100.0` in both patch decorators (lines 299-302 and 354-357) to provide a fixed return value that works regardless of how many times the implementation calls monotonic(), making the test less brittle and decoupled from internal implementation details.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/team_axis/CustomTeamAxis.py`:
- Around line 13-25: The `_prefixed_example` function incorrectly splits command
examples using simple `split(",")` without accounting for commas inside
parentheses, causing multi-parameter commands like `walk(w, 0.2)` to be split
incorrectly and generate malformed prefixed examples. Replace the simple
`split(",")` call with intelligent parsing that tracks parenthesis depth and
only treats commas at depth zero as delimiters. Iterate through the string
character by character, increment depth on opening parentheses and decrement on
closing ones, and only mark a comma as a split point when depth equals zero.
In `@src/team_axis/TeamAxisRegistry.py`:
- Around line 45-48: The custom axes compilation in the list comprehension lacks
error handling, causing any single malformed axis configuration to crash the
entire registration process and interrupt the battle loop. Wrap the
CustomTeamAxis.build_definition(axis) call within a try-except block that
catches exceptions for individual axis builds, logs the error details for that
specific axis, and continues processing remaining axes by only including
successfully built definitions in the final custom_axes list. This ensures that
one bad configuration does not block the entire axis registration flow.
In `@src/ui/TeamAxisTab.py`:
- Around line 282-295: The `_match_state` method currently only validates the
team signature but does not check if the custom axis has actual content,
allowing axes with empty content to be directly enabled and written to the
effective configuration. Add a validation check in the `_match_state` method
after obtaining the axis via `get_axis_class(self.selected_axis_id())` to ensure
the axis content is not empty before returning the success status. This will
prevent incomplete or unconfigured axes from being enabled and avoid execution
failures during battle engagement.
In `@tests/TestCustomChar.py`:
- Around line 255-260: The assertion in the
test_char_hub_contains_fixed_axis_tab method directly checks for a hardcoded
localized string "固定轴" against hub.team_axis_tab.name, which couples the test to
a specific locale and will fail in different language environments. Replace this
localized string assertion with a language-independent check, such as verifying
the tab instance exists or using the objectName() property instead, to ensure
the test remains robust across different internationalization environments
without depending on specific locale settings.
In `@tests/TestTeamAxis.py`:
- Around line 275-279: The test case at lines 275-279 uses a patch context for
CustomCharManager that affects the TeamAxisRegistry's caching behavior, which
can cause cross-test contamination. Add calls to clear_registry_cache() before
entering the patch context (either in setUp or immediately before the with patch
block) and after the test completes (either in tearDown or immediately after the
assertions) to ensure the registry cache is properly isolated and cleaned up
between test runs.
---
Nitpick comments:
In `@tests/TestTeamAxis.py`:
- Around line 299-302: The patch decorator for
`src.team_axis.axes.NanallyZeroJiuyuanHotoriAxis.time.monotonic` uses
`side_effect=(100.0, 100.0)` which restricts the mock to return a value exactly
twice, causing StopIteration if the implementation makes additional calls to
monotonic(). Replace `side_effect=(100.0, 100.0)` with `return_value=100.0` in
both patch decorators (lines 299-302 and 354-357) to provide a fixed return
value that works regardless of how many times the implementation calls
monotonic(), making the test less brittle and decoupled from internal
implementation details.
🪄 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: a2c9a4c7-001b-4d7c-9a3e-ef4538e0f622
📒 Files selected for processing (16)
.gitignoresrc/char/Jiuyuan.pysrc/char/custom/CustomCharManager.pysrc/tasks/trigger/AutoCombatTask.pysrc/team_axis/BaseTeamAxis.pysrc/team_axis/CustomTeamAxis.pysrc/team_axis/README.mdsrc/team_axis/TeamAxisRegistry.pysrc/team_axis/__init__.pysrc/team_axis/axes/NanallyZeroJiuyuanHotoriAxis.pysrc/team_axis/axes/__init__.pysrc/ui/CharHubTab.pysrc/ui/TeamAxisTab.pytests/TestCustomChar.pytests/TestCustomCharCore.pytests/TestTeamAxis.py
|
|
没有必要 当前的combat planner已经可以支撑固定轴的编写,另外引入一个系统没有意义,且当前的创生队已经会根据老板娘的技能来打创生反应了 |
|
Thanks for the review. I addressed the reported issues in
Validation:
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/team_axis/CustomTeamAxis.py (1)
13-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSonarCloud 质量门:
_split_example_commands认知复杂度超标(21 > 15)逻辑本身正确,但 SonarCloud 将此函数标记为 CI 失败。可将引号/转义处理抽取为独立辅助函数以降低复杂度,使该函数只负责括号深度与逗号切分。
♻️ 建议的重构思路
+def _consume_quoted(char: str, quote: str, escape: bool) -> tuple[str, bool]: + if escape: + return quote, False + if char == "\\": + return quote, True + if char == quote: + return "", False + return quote, False + + def _split_example_commands(example: str) -> list[str]: parts = [] current = [] depth = 0 quote = "" escape = False for char in example: if quote: current.append(char) - if escape: - escape = False - elif char == "\\": - escape = True - elif char == quote: - quote = "" + quote, escape = _consume_quoted(char, quote, escape) continue🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/team_axis/CustomTeamAxis.py` around lines 13 - 52, The `_split_example_commands` function exceeds SonarCloud's cognitive complexity threshold (21 > 15) due to nested conditional logic. Extract the quote and escape handling logic (the `if quote:` block and the `if char in ("'", '"'):` block) into a separate helper function that processes character handling when inside or entering quoted strings. This helper function should return the updated quote and escape states along with whether the character should be appended to the current buffer. Refactor the main function to call this helper and keep only the bracket depth tracking and comma-based splitting logic at the top level, which will significantly reduce the cognitive complexity of `_split_example_commands`.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/team_axis/CustomTeamAxis.py`:
- Around line 13-52: The `_split_example_commands` function exceeds SonarCloud's
cognitive complexity threshold (21 > 15) due to nested conditional logic.
Extract the quote and escape handling logic (the `if quote:` block and the `if
char in ("'", '"'):` block) into a separate helper function that processes
character handling when inside or entering quoted strings. This helper function
should return the updated quote and escape states along with whether the
character should be appended to the current buffer. Refactor the main function
to call this helper and keep only the bracket depth tracking and comma-based
splitting logic at the top level, which will significantly reduce the cognitive
complexity of `_split_example_commands`.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 92df4a3d-b2e7-485c-b092-24538ca1a6e0
📒 Files selected for processing (5)
src/team_axis/CustomTeamAxis.pysrc/team_axis/TeamAxisRegistry.pysrc/ui/TeamAxisTab.pytests/TestCustomChar.pytests/TestTeamAxis.py
🚧 Files skipped from review as they are similar to previous changes (2)
- src/ui/TeamAxisTab.py
- tests/TestTeamAxis.py



Summary
新增固定队伍轴的自定义能力。
主要内容:
p1_/p2_/p3_/p4_前缀复用现有出招表指令。Why
原本固定轴只能使用预设队伍,用户无法在界面中自行配置完整队伍轴。
这个改动让用户可以基于 4 个位置分别编写角色出招逻辑,从而自定义自己的固定队伍流程,而不是只依赖内置创生队轴。
Tests
python -m unittest tests.TestTeamAxis tests.TestCustomCharCore tests.TestCustomCharuvx ruff check . --select F,IManual test evidence
录屏:
Summary by CodeRabbit
发布说明