feat: 新增链式战斗系统与相关角色实现#99
Conversation
1. 新增ChainExecutor实现链式战斗流程管理 2. 为多个角色添加链式战斗子类:HotoriChain、ZeroChain、JiuyuanChain、NanallyChain 3. 新增CharFactory链式角色注册配置 4. 为BaseChar添加链式执行支持与辅助方法 5. 为DodgeCounterTrigger添加闪避抑制功能 6. 扩展BaseCombatTask以支持链式战斗流程切换
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough本PR新增链式执行器 ChainExecutor,扩展 BaseChar 协议并集成到 BaseCombatTask,增加闪避抑制,新增并注册 HotoriChain、JiuyuanChain、NanallyChain、ZeroChain 四个链式角色实现。 Changes角色链式执行系统实现
Sequence Diagram(s)sequenceDiagram
participant Task
participant ChainExecutor
participant Char
participant Team
Task->>ChainExecutor: loop(builder)
ChainExecutor->>ChainExecutor: _start(steps)
ChainExecutor->>Char: set _chain_method for target
Char->>Char: perform() executes _chain_method
Char->>Team: update_team_skill_records (when applicable)
Char->>ChainExecutor: step_complete()
ChainExecutor->>ChainExecutor: advance index / build next or stop
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 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: 4
🧹 Nitpick comments (5)
src/char/HotoriChain.py (2)
90-165: ⚡ Quick win提取重复的技能确认逻辑以降低认知复杂度
SonarCloud 标记此函数的认知复杂度为 28(允许上限 15)。主要原因是存在三处几乎相同的"成功退出"代码块(108-117、140-147、151-160),都执行相同的状态设置和角色切换序列。
建议提取公共逻辑到辅助方法:
♻️ 建议重构
+ def _finish_e_start_chain(self): + """E技能启动链完成后的公共处理""" + self._e_used = True + self._e_lockdown = True + self.start_team_skill_window() + self.sleep(0.1) + self.task.chain_executor.step_complete() + self._send_chain_key() + self.switch_next_char() + def chain_e_start_chain(self): # ... 前部逻辑保持不变 ... if self.has_cd("skill"): self.logger.info("chain_e_start_chain: E already in CD, proceeding") - self._e_used = True - self._e_lockdown = True - self.start_team_skill_window() - self.sleep(0.1) - self.task.chain_executor.step_complete() - self._send_chain_key() - self.switch_next_char() - return + self._finish_e_start_chain() + return对其他两处重复代码块进行同样的替换。
🤖 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/char/HotoriChain.py` around lines 90 - 165, The function chain_e_start_chain contains three near-identical "successful exit" blocks; extract that repeated sequence (setting self._e_used, self._e_lockdown, calling start_team_skill_window, sleep, self.task.chain_executor.step_complete, self._send_chain_key, and self.switch_next_char) into a helper method (e.g., _finalize_e_chain or _handle_successful_e_cast) and replace each duplicated block with a single call to that helper from chain_e_start_chain to reduce cognitive complexity and duplication.
7-21: ⚡ Quick win将类级别的链式步骤定义改为不可变类型
STARTUP_CHAIN和WARMUP_CHAIN作为类级别的可变列表,存在被意外修改的风险。建议改用元组(tuple)确保不可变性,这也符合其作为常量配置的语义。♻️ 建议修改
class HotoriChain(Hotori): - STARTUP_CHAIN = [ + STARTUP_CHAIN = ( ("HotoriChain", "chain_e_start_chain"), ("ZeroChain", "chain_q_e_wait"), ("JiuyuanChain", "chain_intro_only"), ("NanallyChain", "chain_e_q_6s_swap"), ("JiuyuanChain", "chain_q_e_heavy"), ("HotoriChain", "chain_q_na"), - ] - WARMUP_CHAIN = [ + ) + WARMUP_CHAIN = ( ("ZeroChain", "chain_nop"), ("JiuyuanChain", "chain_intro_only"), ("ZeroChain", "chain_e_only"), ("NanallyChain", "chain_intro_e_q_10s_swap"), ("JiuyuanChain", "chain_q_e_heavy"), - ] + )🤖 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/char/HotoriChain.py` around lines 7 - 21, Change the class-level mutable lists STARTUP_CHAIN and WARMUP_CHAIN in HotoriChain.py to immutable tuples to prevent accidental modification and reflect their constant configuration nature; replace the list literals for STARTUP_CHAIN and WARMUP_CHAIN with tuple literals (use parentheses instead of square brackets) wherever they are defined in the HotoriChain class so any runtime code referencing STARTUP_CHAIN or WARMUP_CHAIN receives an immutable sequence.src/char/NanallyChain.py (2)
131-209: 🏗️ Heavy lift认知复杂度高(51 vs 15),且与
chain_e_q_6s_swap存在代码重复。两个链式方法共享相同的模式:
- 角色切换等待逻辑(lines 173-181, 200-205 与 88-96, 120-125 相同)
- HotoriChain 查找(line 143 与 line 19)
- Hotori 点击循环(lines 186-191 与 101-111)
建议将这些重复模式提取到
NanallyChain或基类中的共享方法,两个链式方法可以复用。🤖 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/char/NanallyChain.py` around lines 131 - 209, The method chain_intro_e_q_10s_swap has high cognitive complexity and duplicates logic from chain_e_q_6s_swap (character-switch waiting, HotoriChain lookup, and Hotori click loop); extract shared patterns into small helper methods on NanallyChain or its base: e.g., _wait_for_char_index(self, index, timeout) to encapsulate the switch-wait loops used around self.task.is_char_at_index, _find_hotori(self) to return the HotoriChain instance/key, and _play_hotori_clicks(self, hotori, duration) to run the repeated hotori.click()/sleep() loop; then replace the duplicated blocks in chain_intro_e_q_10s_swap and chain_e_q_6s_swap with calls to these helpers and keep existing unique logic (skill/ultimate handling) intact so behavior is unchanged.
15-129: 🏗️ Heavy lift认知复杂度过高(70 vs 15),建议拆分为多个辅助方法。
此方法包含多个可复用的逻辑块:E技能施放(lines 24-56)、角色切换等待(lines 88-96, 120-125)、普攻循环(lines 62-78)。将这些抽取为独立方法可显著降低复杂度并提高可读性。
♻️ 建议的重构方向
def _switch_to_char(self, char, total_deadline): """尝试切换到指定角色,返回是否成功""" key = self._get_char_key(char.__class__.__name__) if key: self.task.send_key(key) switch_deadline = time.time() + 1.0 while (not self.task.is_char_at_index(char.index) and time.time() < switch_deadline and time.time() < total_deadline): self.click() self.sleep(0.01) return self.task.is_char_at_index(char.index) def _try_cast_e_with_timeout(self, deadline): """尝试在截止时间内施放E技能,返回是否成功""" while time.time() < deadline: self.task.sleep_check() if not self.task.is_char_at_index(self.index): self.click() self.sleep(0.01) continue clicked, _, _ = self.click_skill() if clicked: return True self.click() self.sleep(0.05) return False def chain_e_q_6s_swap(self): # 使用上述辅助方法简化主流程 ...🤖 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/char/NanallyChain.py` around lines 15 - 129, 方法 chain_e_q_6s_swap 的认知复杂度过高;请把可复用的逻辑块抽成小方法:将 E 技能施放循环(当前在 chain_e_q_6s_swap 中使用 e_deadline 的块)提取为 _try_cast_e_with_timeout(self, deadline) 并返回是否成功, 将角色切换等待逻辑(目前重复出现在 hotori 切换和 Nanally 切换处)提取为 _switch_to_char(self, char_or_name, total_deadline) 并返回是否切换成功,以及将普攻循环(NA 循环那段定时与日志逻辑)提取为 _run_na_loop(self, duration, total_deadline, q_casted_ref) 并在主方法中用这些小函数替换原有代码以简化流程并降低复杂度;确保保留原有的副作用(self.task.sleep_check, self.click, self.click_skill, self.click_ultimate, self.task._combat_settle.time 修改, self.task.send_key, self.task.next_frame, self.logger 调用 和 self.task.chain_executor.step_complete()/ _send_chain_key()/switch_next_char())。src/char/JiuyuanChain.py (1)
31-67: 💤 Low value认知复杂度略超阈值(19 vs 15)。
SonarCloud 标记此函数认知复杂度为 19。可考虑将开场处理(lines 32-38)和终极技轮询(lines 39-47)抽取为辅助方法,以降低主函数的嵌套层级。
♻️ 可选重构示例
+ def _wait_intro_until_ultimate(self): + """等待开场动画直到终极技可用""" + if not self.has_intro: + return + start = time.time() + while time.time() - start < self.INTRO_MOTION_FREEZE_DURATION: + self.click() + if self.ultimate_available(): + break + self.sleep(0.1) + def chain_q_e_heavy(self): - if self.has_intro: - start = time.time() - while time.time() - start < self.INTRO_MOTION_FREEZE_DURATION: - self.click() - if self.ultimate_available(): - break - self.sleep(0.1) + self._wait_intro_until_ultimate() q_deadline = time.time() + 0.3 # ... rest of the method🤖 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/char/JiuyuanChain.py` around lines 31 - 67, The function chain_q_e_heavy has high cognitive complexity; extract the intro motion-freeze block (the has_intro loop that repeatedly clicks until ultimate_available or timeout) into a helper method (e.g., _handle_intro_motion_freeze) and extract the short ultimate-polling loop that attempts an ultimate click (the q_deadline loop that sets task._combat_settle.time, sleeps, clicks and calls click_ultimate) into another helper (e.g., _poll_and_click_ultimate); then simplify chain_q_e_heavy to call these two helpers before continuing with the remaining click_skill loop so nesting and branching are reduced while preserving behavior (ensure the helpers return any signals needed by chain_q_e_heavy such as whether ultimate was triggered).
🤖 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/char/HotoriChain.py`:
- Around line 23-28: The attribute team_skill_window_start is used by methods
like team_skill_window_elapsed and waiting_for_team_skills but isn't declared in
__init__, risking an AttributeError; add an explicit initialization (e.g.
self.team_skill_window_start = None or 0) inside __init__ alongside
self.team_skill_records and the _e_* fields so the attribute always exists, and
leave start_team_skill_window() and clear_team_skill_records() to set/clear its
value as they already do.
- Around line 180-187: The infinite loop in HotoriChain.run that relies on
ultimate_available() and click_ultimate() needs a timeout safeguard: add a
max-wait timer (e.g., start = time.time(), timeout_seconds = X) inside the loop
that calls task.sleep_check(), and after each iteration check if time.time() -
start > timeout_seconds then log an error via self.logger.error (including
current state), break or raise a controlled exception so the loop can exit
safely; update references in the loop where ultimate_available(),
click_ultimate(send_click=True), self.click(), and self.sleep(0.1) are used to
include this timeout check and cleanup behavior.
In `@src/char/ZeroChain.py`:
- Around line 38-47: The infinite loop in the ZeroChain method relies solely on
click_skill() to return clicked=True and lacks a timeout; add a timeout guard
(e.g., record start = time.monotonic() and check elapsed against a configurable
timeout) around the while True loop that calls click_skill(), click(), sleep(),
etc., and when the timeout is exceeded log/warn and perform a safe exit path
(call task.chain_executor.step_complete() or an error handler, then call
_send_chain_key() and switch_next_char() or otherwise return) so the method
never blocks indefinitely; update the loop that uses click_skill(), click(),
sleep() to break on timeout using the chosen timeout variable.
In `@src/combat/ChainExecutor.py`:
- Around line 31-36: The chain startup does not guard against builder()
returning None or an empty list, causing an IndexError when accessing
self.steps[0]; update the startup logic in ChainExecutor where builder() is
called (the local variable steps and the methods _start and subsequent access to
self.steps and first_char/first_method) to explicitly validate the result of
builder() (treat None as []), check for emptiness, and handle that case by
logging/returning early (safe no-op) instead of proceeding to access steps[0];
apply the same guard to the similar block around the code referenced for lines
42-47.
---
Nitpick comments:
In `@src/char/HotoriChain.py`:
- Around line 90-165: The function chain_e_start_chain contains three
near-identical "successful exit" blocks; extract that repeated sequence (setting
self._e_used, self._e_lockdown, calling start_team_skill_window, sleep,
self.task.chain_executor.step_complete, self._send_chain_key, and
self.switch_next_char) into a helper method (e.g., _finalize_e_chain or
_handle_successful_e_cast) and replace each duplicated block with a single call
to that helper from chain_e_start_chain to reduce cognitive complexity and
duplication.
- Around line 7-21: Change the class-level mutable lists STARTUP_CHAIN and
WARMUP_CHAIN in HotoriChain.py to immutable tuples to prevent accidental
modification and reflect their constant configuration nature; replace the list
literals for STARTUP_CHAIN and WARMUP_CHAIN with tuple literals (use parentheses
instead of square brackets) wherever they are defined in the HotoriChain class
so any runtime code referencing STARTUP_CHAIN or WARMUP_CHAIN receives an
immutable sequence.
In `@src/char/JiuyuanChain.py`:
- Around line 31-67: The function chain_q_e_heavy has high cognitive complexity;
extract the intro motion-freeze block (the has_intro loop that repeatedly clicks
until ultimate_available or timeout) into a helper method (e.g.,
_handle_intro_motion_freeze) and extract the short ultimate-polling loop that
attempts an ultimate click (the q_deadline loop that sets
task._combat_settle.time, sleeps, clicks and calls click_ultimate) into another
helper (e.g., _poll_and_click_ultimate); then simplify chain_q_e_heavy to call
these two helpers before continuing with the remaining click_skill loop so
nesting and branching are reduced while preserving behavior (ensure the helpers
return any signals needed by chain_q_e_heavy such as whether ultimate was
triggered).
In `@src/char/NanallyChain.py`:
- Around line 131-209: The method chain_intro_e_q_10s_swap has high cognitive
complexity and duplicates logic from chain_e_q_6s_swap (character-switch
waiting, HotoriChain lookup, and Hotori click loop); extract shared patterns
into small helper methods on NanallyChain or its base: e.g.,
_wait_for_char_index(self, index, timeout) to encapsulate the switch-wait loops
used around self.task.is_char_at_index, _find_hotori(self) to return the
HotoriChain instance/key, and _play_hotori_clicks(self, hotori, duration) to run
the repeated hotori.click()/sleep() loop; then replace the duplicated blocks in
chain_intro_e_q_10s_swap and chain_e_q_6s_swap with calls to these helpers and
keep existing unique logic (skill/ultimate handling) intact so behavior is
unchanged.
- Around line 15-129: 方法 chain_e_q_6s_swap 的认知复杂度过高;请把可复用的逻辑块抽成小方法:将 E
技能施放循环(当前在 chain_e_q_6s_swap 中使用 e_deadline 的块)提取为
_try_cast_e_with_timeout(self, deadline) 并返回是否成功, 将角色切换等待逻辑(目前重复出现在 hotori 切换和
Nanally 切换处)提取为 _switch_to_char(self, char_or_name, total_deadline)
并返回是否切换成功,以及将普攻循环(NA 循环那段定时与日志逻辑)提取为 _run_na_loop(self, duration,
total_deadline, q_casted_ref)
并在主方法中用这些小函数替换原有代码以简化流程并降低复杂度;确保保留原有的副作用(self.task.sleep_check, self.click,
self.click_skill, self.click_ultimate, self.task._combat_settle.time 修改,
self.task.send_key, self.task.next_frame, self.logger 调用 和
self.task.chain_executor.step_complete()/ _send_chain_key()/switch_next_char())。
🪄 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: 809202c1-7a81-4c38-9199-d1738769e06a
📒 Files selected for processing (9)
src/char/BaseChar.pysrc/char/CharFactory.pysrc/char/HotoriChain.pysrc/char/JiuyuanChain.pysrc/char/NanallyChain.pysrc/char/ZeroChain.pysrc/combat/BaseCombatTask.pysrc/combat/ChainExecutor.pysrc/sound_trigger/DodgeCounterTrigger.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
Findings [P1] 通用链式执行器直接硬编码角色:ChainExecutor.step_complete() 在 ChainExecutor.py (line 61) 查找 "HotoriChain" 并调用其记录逻辑。这把特定角色机制塞进了通用战斗调度层,明显污染架构。后续每加一种链式队伍,都可能继续改 executor,维护性会快速变差。 [P2] 链式调度状态侵入 BaseChar / BaseCombatTask:BaseChar.perform() 通过动态字符串 _chain_method 执行方法,见 BaseChar.py (line 128);切人逻辑又读取 _pending_anchor,见 BaseCombatTask.py (line 491)。这让所有角色都承担链式系统的隐藏约定,且缺少类型约束和生命周期边界。建议让 ChainExecutor 持有显式 Step/回调对象,角色只暴露清晰的链式动作接口。 [P2] 角色实现直接改任务内部状态:多处链式角色写 self.task._combat_settle.time = None,例如 HotoriChain.py (line 180)、JiuyuanChain.py (line 40)、NanallyChain.py (line 47)。这是对 CombatCheck 内部 settle 机制的绕过,建议封装成 BaseCombatTask.allow_ultimate_during_settle() 之类的语义方法。 [P3] git diff --check 发现 ZeroChain.py (line 27) 和 ZeroChain.py (line 37) 有 trailing whitespace。 总体判断 |
|
简单来说 实现不够优雅 我之后有时间再看一下有没有机会重构进去 |
- [重构] ChainExecutor: 清除所有角色硬编码(如 HotoriChain 特判),回归纯粹的链式调度器职责。 - [重构] HotoriChain: 引入 `time_to_next_burst` 时间 API,接管 15s/20s 轴的时间裁判权,精准扣除大招时停。同时通过 `on_chain_step_complete` 实现控制反转,自主管理 E 技能锁定。 - [重构] NanallyChain: 移除死循环与硬编码倒计时,重构为基于状态机的 1.2s/0.8s 无阻塞极限合轴。新增 `is_char_at_index` 真实在场判定,解决hotori错误释放E技能的问题。 - [重构] JiuyuanChain: 剥离跨角色越权操作,移除对 Hotori 私有变量的直接修改。
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/combat/ChainExecutor.py (1)
31-36:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win为链启动补充空步骤保护,避免直接崩溃。
Line 35 与 Line 46 在
builder()返回None或空列表时会触发异常(越界/解包),链式流程会被直接打断。建议在loop()与_start()双重兜底并安全返回。建议修复
def loop(self, builder): import time self._builder = builder - steps = builder() + steps = builder() or [] + if not steps: + self.active = False + self.steps = [] + self.current_index = 0 + self.task.log_info("chain builder returned no steps, skip start") + return self._chain_start_time = time.time() self._last_step_time = time.time() self._start(steps) first_char, first_method = self.steps[0] def _start(self, steps): + if not steps: + self.active = False + self.steps = [] + self.current_index = 0 + return self.steps = steps self.current_index = 0 self.active = True first_char, first_method = steps[0]Also applies to: 42-47
🤖 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/combat/ChainExecutor.py` around lines 31 - 36, The code currently assumes builder() returns a non-empty list and directly indexes/unpacks steps (e.g., steps = builder(), self._start(steps), first_char, first_method = self.steps[0]) which raises on None/empty; update _start() and loop() to defensively handle a falsy or empty steps: if steps is None or len(steps) == 0, log/return safely and do not proceed to unpack or index self.steps; also set self.steps to an empty list before returning so other code (like task.get_current_char calls) sees a consistent state. Ensure checks are applied around the builder() call and at the top of _start() to short-circuit execution when no steps are present.
🧹 Nitpick comments (5)
src/char/Hotori.py (1)
156-158: 💤 Low value空代码块需要填充或移除
on_chain_step_complete方法中存在空的pass语句。如果这是有意的空操作(例如作为子类覆盖的占位符),应添加注释说明意图;否则应移除整个条件分支或实现实际逻辑。♻️ 建议:添加注释或移除空块
def on_chain_step_complete(self): if self.team_skill_window_start > 0: - pass + # 基类中无需处理,由子类 HotoriChain 覆盖实现 + pass或者如果不需要此方法,可以完全移除:
- def on_chain_step_complete(self): - if self.team_skill_window_start > 0: - pass -🤖 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/char/Hotori.py` around lines 156 - 158, The conditional in on_chain_step_complete contains an empty pass for the case when team_skill_window_start > 0; either implement the intended behavior or remove the dead branch — if this is intentionally a placeholder for subclasses, replace the bare pass with a short clarifying comment like "# intentionally no-op; override in subclasses" or document the intent; otherwise delete the if-block or add the actual logic that should run when team_skill_window_start > 0 (refer to on_chain_step_complete and the team_skill_window_start attribute to locate the code).src/char/HotoriChain.py (1)
216-216: ⚖️ Poor tradeoff直接修改任务内部状态应封装为语义化方法
直接设置
self.task._combat_settle.time = None绕过了 CombatCheck/settle 机制。这种对内部字段的直接修改增加了隐式耦合,使代码更难维护。建议在
BaseCombatTask中封装为语义化方法,如allow_ultimate_during_settle()或reset_combat_settle(),以明确表达意图并集中管理该逻辑。🤖 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/char/HotoriChain.py` at line 216, The code directly mutates an internal field (self.task._combat_settle.time = None) which breaks encapsulation and the CombatCheck/settle flow; instead add a semantic method on BaseCombatTask (e.g., reset_combat_settle() or allow_ultimate_during_settle()) that performs the necessary state change and any related checks, then replace the direct assignment in HotoriChain with a call to that new method (use BaseCombatTask.reset_combat_settle() or the chosen method name to locate and update the call site and to centralize the logic).src/char/NanallyChain.py (3)
36-37: ⚖️ Poor tradeoff直接修改任务内部状态应封装为语义化方法
与
HotoriChain中相同,直接设置self.task._combat_settle.time = None绕过了 settle 机制。建议封装到BaseCombatTask的公开方法中。Also applies to: 67-68
🤖 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/char/NanallyChain.py` around lines 36 - 37, Directly manipulating self.task._combat_settle.time bypasses the settle mechanism; instead add a semantic public method on BaseCombatTask (e.g., clear_combat_settle() or mark_settle_cleared()) that encapsulates setting the internal settle state, then replace direct assignments (both occurrences where self.task._combat_settle.time = None in NanallyChain, including the one before click_ultimate and the similar one at lines ~67-68) with calls to that new BaseCombatTask method; mirror the pattern used in HotoriChain to keep behavior consistent and preserve encapsulation.
23-27: ⚡ Quick win合并嵌套的 if 语句
连续的
if self.skill_available()和if self.click_skill()可以合并为单个条件表达式,减少嵌套层级。♻️ 建议修改
if self.skill_available(): - if self.click_skill(): - _last_e_time = time.time() - self.logger.info("Nanally E released for copy") - break + if self.click_skill(): + _last_e_time = time.time() + self.logger.info("Nanally E released for copy") + break或使用
and合并:- if self.skill_available(): - if self.click_skill(): - _last_e_time = time.time() - self.logger.info("Nanally E released for copy") - break + if self.skill_available() and self.click_skill(): + _last_e_time = time.time() + self.logger.info("Nanally E released for copy") + break🤖 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/char/NanallyChain.py` around lines 23 - 27, Merge the nested conditionals in the NanallyChain method (the block using self.skill_available() and self.click_skill()) into a single if statement using an and expression (e.g., if self.skill_available() and self.click_skill():) so that click_skill() is only called when skill_available() is true, and preserve the existing actions (_last_e_time assignment, self.logger.info call, and break) inside that single block.
62-68: ⚡ Quick win合并嵌套的 if 语句
if self.task.is_char_at_index(self.index)内的if self.skill_available()和if self.click_skill()可以合并。♻️ 建议修改
if self.task.is_char_at_index(self.index): - if self.skill_available(): - if self.click_skill(): - _last_e_time = time.time() + if self.skill_available() and self.click_skill(): + _last_e_time = time.time() if self.ultimate_available() and time.time() - _last_e_time >= 0.4:🤖 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/char/NanallyChain.py` around lines 62 - 68, Inside the block guarded by self.task.is_char_at_index(self.index), merge the nested skill checks by replacing the separate if self.skill_available(): if self.click_skill(): sequence with a single conditional that calls click_skill only when both self.skill_available() and self.click_skill() are true; update _last_e_time only when the combined condition succeeds. Keep the ultimate logic intact (the check using self.ultimate_available() and time.time() - _last_e_time >= 0.4, setting self.task._combat_settle.time = None and calling self.click_ultimate()) so behavior/timing is preserved.
🤖 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/char/BaseChar.py`:
- Around line 602-603: 在 BaseChar 类的 on_chain_step_complete 方法中补充一条明确说明该空实现为默认
no-op 拓展点的文档字符串或内联注释(例如在函数体内添加 """No-op hook; override in subclasses to handle
chain step completion.""" 或 # intentional no-op hook for
subclasses),以表明这是有意为之并避免 Sonar 静态检查误报;定位符号:方法名 on_chain_step_complete,类名
BaseChar。
In `@src/char/NanallyChain.py`:
- Around line 15-107: chain_dynamic_standby has excessive cognitive complexity
(≈75); refactor by extracting smaller helpers and delegating responsibilities:
implement _try_initial_e_release(self) to run the initial "E" release block
(return last_e_time or 0), implement _try_q_after_e(self, last_e_time) to
attempt the Q after E, implement _standby_loop_iteration(self, hotori,
last_e_time) to encapsulate the inner per-iteration logic that handles clicks,
skill/ultimate checks and sending Nanally key (return updated last_e_time and a
flag to indicate handing off), and implement _switch_to_hotori_and_back(self,
hotori) to perform sending Hotori key, waiting for switch, executing Hotori
burst, and switching back; then simplify chain_dynamic_standby to call these
helpers in sequence, pass/return _last_e_time and hotori as needed, and preserve
all existing timing, logging and task.sleep_check() behavior to keep semantics
identical.
---
Duplicate comments:
In `@src/combat/ChainExecutor.py`:
- Around line 31-36: The code currently assumes builder() returns a non-empty
list and directly indexes/unpacks steps (e.g., steps = builder(),
self._start(steps), first_char, first_method = self.steps[0]) which raises on
None/empty; update _start() and loop() to defensively handle a falsy or empty
steps: if steps is None or len(steps) == 0, log/return safely and do not proceed
to unpack or index self.steps; also set self.steps to an empty list before
returning so other code (like task.get_current_char calls) sees a consistent
state. Ensure checks are applied around the builder() call and at the top of
_start() to short-circuit execution when no steps are present.
---
Nitpick comments:
In `@src/char/Hotori.py`:
- Around line 156-158: The conditional in on_chain_step_complete contains an
empty pass for the case when team_skill_window_start > 0; either implement the
intended behavior or remove the dead branch — if this is intentionally a
placeholder for subclasses, replace the bare pass with a short clarifying
comment like "# intentionally no-op; override in subclasses" or document the
intent; otherwise delete the if-block or add the actual logic that should run
when team_skill_window_start > 0 (refer to on_chain_step_complete and the
team_skill_window_start attribute to locate the code).
In `@src/char/HotoriChain.py`:
- Line 216: The code directly mutates an internal field
(self.task._combat_settle.time = None) which breaks encapsulation and the
CombatCheck/settle flow; instead add a semantic method on BaseCombatTask (e.g.,
reset_combat_settle() or allow_ultimate_during_settle()) that performs the
necessary state change and any related checks, then replace the direct
assignment in HotoriChain with a call to that new method (use
BaseCombatTask.reset_combat_settle() or the chosen method name to locate and
update the call site and to centralize the logic).
In `@src/char/NanallyChain.py`:
- Around line 36-37: Directly manipulating self.task._combat_settle.time
bypasses the settle mechanism; instead add a semantic public method on
BaseCombatTask (e.g., clear_combat_settle() or mark_settle_cleared()) that
encapsulates setting the internal settle state, then replace direct assignments
(both occurrences where self.task._combat_settle.time = None in NanallyChain,
including the one before click_ultimate and the similar one at lines ~67-68)
with calls to that new BaseCombatTask method; mirror the pattern used in
HotoriChain to keep behavior consistent and preserve encapsulation.
- Around line 23-27: Merge the nested conditionals in the NanallyChain method
(the block using self.skill_available() and self.click_skill()) into a single if
statement using an and expression (e.g., if self.skill_available() and
self.click_skill():) so that click_skill() is only called when skill_available()
is true, and preserve the existing actions (_last_e_time assignment,
self.logger.info call, and break) inside that single block.
- Around line 62-68: Inside the block guarded by
self.task.is_char_at_index(self.index), merge the nested skill checks by
replacing the separate if self.skill_available(): if self.click_skill():
sequence with a single conditional that calls click_skill only when both
self.skill_available() and self.click_skill() are true; update _last_e_time only
when the combined condition succeeds. Keep the ultimate logic intact (the check
using self.ultimate_available() and time.time() - _last_e_time >= 0.4, setting
self.task._combat_settle.time = None and calling self.click_ultimate()) so
behavior/timing is preserved.
🪄 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: 003351b3-bc27-4cd6-a30a-89d7c10384d9
📒 Files selected for processing (6)
src/char/BaseChar.pysrc/char/Hotori.pysrc/char/HotoriChain.pysrc/char/JiuyuanChain.pysrc/char/NanallyChain.pysrc/combat/ChainExecutor.py
💤 Files with no reviewable changes (1)
- src/char/JiuyuanChain.py
1. 新增ChainLoader实现角色策略化加载与替换 2. 新增全局连招策略配置UI与后端逻辑 3. 为多个角色链添加角色就位等待逻辑 4. 优化内置连招注册表过滤规则 5. 取消九原连招的闪避压制问题 6. 优化娜娜莉连招的技能释放时机判断
1. 统一链内常量配置,替换硬编码 2. 新增CD确认机制,避免技能释放误判 3. 优化切人等待逻辑,增加超时保护 4. 重构NanallyChain动态驻场逻辑,修复切人循环问题
你看看还有什么问题 |
- 新增 get_all_character_names() 合并内置+自定义角色名 - 新增重新关联角色功能
|





Summary by CodeRabbit