This repository was archived by the owner on Sep 20, 2026. It is now read-only.
fix(combat-insights): stabilize recap impact tooltips - #212
Merged
Merged
Conversation
…nt opens Five distinct defects behind the recurring Combat Impact orphan flash and missing-details reports, root-caused against the Tooltip_Aux_P prefab dumped from the game's asset bundles: - CanvasGroupGate.Create fails soft (null) when AddComponent lands on a controller whose Destroy is pending, instead of throwing NRE into the feature's catch-all; TryOpen conceals and rolls back with a typed NativePairedTooltipOpenFailure reason the view logs at Warning. - Transient open failures requeue the pending show (bounded per hover revision) instead of suppressing details until pointer exit. - The controller-root shell gate is removed: the root is a world-space Transform above the prefab's nested Canvas, so a CanvasGroup there never affected rendering. auxParent (Tooltip_Aux_Content) owns the complete visual tree and its gate is the one effective concealment. - CancelPreparedAuxiliary keeps the auxParent gate closed through native teardown; PrepareAuxiliary reuses a matching held gate closed instead of restore-then-recreate. - Owned gates DestroyImmediate on restore: a deferred Destroy left a corpse that a same-frame re-prepare adopted via GetComponent, silently losing the gate at frame end — the ungated native fade-in was the one-frame white flash on Legendary frames. Adds a card-identified Debug interaction trail (interaction.traced) plus probes for silent exits (pending_show_blocked/aborted, unmatched shows). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regen totals now match the unsigned total format used by Shield/Heal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens the PostCombatImpact “Combat Impact” recap tooltip takeover flow when reusing the game’s native Auxiliary Tooltip, addressing same-frame lifecycle races (prepare/fade-in/hide/destroy) that could previously lead to a header-only native shell or a lost tooltip due to gate creation failures. It also normalizes the displayed formatting for total applied regen.
Changes:
- Make
NativePairedTooltipHostreuse and hold anauxParent-scoped visibility gate through stale teardown, add typedTryOpenfailure reporting, and fail soft on dying controllers (including immediate cleanup of owned gates). - Add bounded transient re-request behavior + richer interaction tracing/reason codes in PostCombatImpact when the native auxiliary is temporarily unusable.
- Remove the redundant “+” from the “Applied Regen total” metric formatting and update tests accordingly.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/PostCombatImpact.Tests/CombatImpactMetricFormatterTests.cs | Updates expected strings for regen totals to match new sign/format behavior. |
| tests/Architecture.Tests/NativePairedTooltipArchitectureTests.cs | Adds/extends architecture tests to lock in auxParent gating, cancel/release ordering, soft-failure semantics, and bounded retry behavior. |
| src/BazaarPlusPlus/Patches/PostCombatImpact/NativePostCombatImpactTooltipView.cs | Logs previously-silent show rejections and maps host open-failures to feature reason codes. |
| src/BazaarPlusPlus/GameInterop/Tooltips/NativePairedTooltipHost.cs | Core stabilization: auxParent gate reuse/holding, conceal-before-rollback, soft gate creation, DestroyImmediate for owned gates, and typed open-failure reporting. |
| src/BazaarPlusPlus/GameInterop/Tooltips/NativePairedTooltipContracts.cs | Introduces NativePairedTooltipOpenFailure enum for typed TryOpen failure reporting. |
| src/BazaarPlusPlus/Game/PostCombatImpact/PostCombatImpactLogEvents.cs | Adds new reason codes and a card-identified interaction trace event/fields. |
| src/BazaarPlusPlus/Game/PostCombatImpact/PostCombatImpactController.cs | Adds bounded transient retries on show failure and richer per-card interaction tracing. |
| src/BazaarPlusPlus/Game/PostCombatImpact/Data/CombatImpactMetricFormatter.cs | Suppresses “+” sign for RegenApplyAmount applied-effect attribute changes. |
| docs/drafts/2026-07-30-recap-right-click-not-received.md | Adds detailed investigation notes and timeline for the tooltip race regressions/fixes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Recap 中的 Combat Impact 复用游戏原生 Auxiliary Tooltip。快速切换卡牌时,原生 tooltip 的异步创建、淡入、销毁与 BPP 的准备/接管可能落在同一帧,先后出现两类用户可见故障:
Combat Impact标题的白色原生框体;CanvasGroupGate构造阶段的NullReferenceException。本 PR 基于实际
Tooltip_Aux_Pprefab 结构和运行日志修复该竞态,并统一 Regen 总量的数字格式。改动重点
auxParentgate 覆盖完整原生视觉树,并在 stale request teardown 期间持续保持关闭。+。协作过程
sequenceDiagram participant U as 用户实机验收 participant C as Codex participant F as Claude Code / Fable participant G as 游戏原生 Tooltip U->>C: 报告偶发 header-only 白框 C->>G: 先修正取消路径的 conceal/restore 顺序 U->>C: 白框仍出现,快速切换后 tooltip 还会完全消失 C->>G: 尝试 controller-root 外层 gate G-->>C: 运行日志暴露 CanvasGroupGate NRE U->>F: 委托基于 prefab 与日志重新定位 F->>G: 确认完整视觉树属于 auxParent,发现 deferred Destroy 同帧复用竞态 F->>C: 改为 held auxParent gate + DestroyImmediate + bounded retry C->>G: 独立复核 diff、运行日志和全部测试门禁方案讨论
ADR-001:可见性 gate 应挂在哪里
Context:原先假定
auxParent只包含正文,因此尝试在 controller root 上增加独立 CanvasGroup。Considered Options:
auxParent上的 BPP gate,直到 teardown 或下一次合法 show 接管。Decision:选择 3。实际 prefab 中
Tooltip_Aux_Content(auxParent)拥有 Background、TitleText、BodyText 和 Divider;controller root 位于嵌套 Canvas 之外,root CanvasGroup 不会传播到真实视觉树。Consequences:取消路径不再提前恢复 gate;同一 controller 的快速重用直接复用仍关闭的 gate,foreign native show 则通过
ReleasePrepared恢复。ADR-002:处理 Unity deferred Destroy
Context:
Object.Destroy到帧末才移除组件。快速切换在同一帧重新 prepare 时,GetComponent<CanvasGroup>()会拿到即将销毁的旧 gate;帧末 gate 消失后,原生淡入变成无保护状态。controller 本身正在销毁时,AddComponent还可能返回 Unity 的 dead reference,随后读取属性触发 NRE。Decision:BPP 自有 gate 在恢复时使用
DestroyImmediate;gate 创建允许返回失败,并由TryOpen输出明确的NativePairedTooltipOpenFailure。Consequences:同帧重建不会继承尸体组件;临时的 dying-controller failure 会在当前 hover 内最多重试两次,持续故障仍会停止,避免无限 hide/show 循环。
最终方案
sequenceDiagram participant H as Hover request participant N as Native Auxiliary Tooltip participant P as NativePairedTooltipSession H->>N: request show N->>P: Harmony prefix / PrepareAuxiliary P->>P: capture native state and close/reuse auxParent gate N->>N: native position + fade + HasShown P->>P: TryOpen and build complete paired content alt live controller and complete content P->>P: reveal gate after layout settles else controller is dying P->>P: conceal + rollback with typed failure H->>N: bounded re-request else stale request P->>P: keep gate closed through native teardown endCombat Impact 的 feature 层仍只负责请求状态、重试预算和 reason-code 日志;原生 geometry、gate 与恢复生命周期继续由
GameInterop/Tooltips的共享 session 管理。验证情况
./run.sh format-check./run.sh test(全量测试通过)NativePairedTooltipHost.Tests:29/29PostCombatImpact.Tests:149/149Architecture.Tests:153/153git diff --checkrecap_hover_observed → shown/settled → dismissed,未再出现CanvasGroupGateNRE 或tooltip_render_exception已知局限 / 后续工作