Skip to content

Commit 1899089

Browse files
committed
Formula Combo mode, better optimize algo, better inventory, formula fix, evaluation card fix, tour fix
1 parent ff48839 commit 1899089

File tree

70 files changed

+7844
-2981
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+7844
-2981
lines changed

docs/TODO.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- 洗词条推荐
2+
- 队伍总伤优化,3队伍同时优化
3+
- 圣遗物匹配度
4+
- 加解锁管理
5+
- 原魔抗性

docs/dmg-tracker/inazuma.yaml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,6 @@
9797
created: "2026-02-25"
9898
resolved: "2026-02-26"
9999

100-
- id: chiori-c2-kinu-coordinated
101-
entity: chiori
102-
rule: S8
103-
status: wont-do
104-
category: missing-formula
105-
summary: >
106-
C2 Kinu periodic coordinated attacks not modeled.
107-
detail: >
108-
Hard to combine with per-hit triggered damage. Periodic coordinated attack
109-
with complex interaction model.
110-
created: "2026-02-25"
111-
resolved: "2026-02-25"
112-
113100
- id: kirara-c4-shield-coordinated
114101
entity: kirara
115102
rule: S8

docs/formulav2-adaptations.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Formula v2 — Design Adaptations
2+
3+
Deviations from the original `formulav2.md` proposal made during implementation.
4+
5+
---
6+
7+
## 1. `evaluateWithReaction()``createReactionVariant()` + pipeline threading
8+
9+
**Design**: A standalone `evaluateWithReaction()` function that takes a formula, target reaction, stats, and context, then dispatches to the correct variant and calls `.calc()`.
10+
11+
**Implementation**: Instead of a single evaluation function, we:
12+
1. Added `createReactionVariant()` as a formula-level utility (creates the variant without evaluating).
13+
2. Threaded `ReactionOverride` through the existing evaluation pipeline (`CharacterBase.getDamageResult``CharBuild.getDamageResult``TeamBuild.getDamageResult`).
14+
15+
**Reason**: The display pipeline (`getDisplayParts`) needs access to the variant formula to produce `DisplayPart` breakdowns for the UI. A pure evaluation function would bypass this. By threading the override through the pipeline, both `.calc()` and `.display()` use the correct variant, and the existing stat resolution (5-phase pipeline) is fully preserved.
16+
17+
## 2. `createDirect()` factory method added
18+
19+
**Design**: Only `createAmplified()` and `createCatalyzed()` factory methods.
20+
21+
**Implementation**: Added `createDirect()` as well, returning a `DirectFormula` with `reaction: "none"`.
22+
23+
**Reason**: Needed for the reverse case — if a formula already has a reaction tag and the user selects "none", we need to downgrade it to a direct variant.
24+
25+
## 3. Per-part reaction resolution via `resolvePartReaction()`
26+
27+
**Design**: Mentions per-part `eligibleReactions` but doesn't specify the resolution logic.
28+
29+
**Implementation**: Added `resolvePartReaction(override, partIndex, eligibleReactions)` utility that resolves effective reaction for each part using a three-tier priority:
30+
1. Explicit per-part override (`partReactions[idx]`)
31+
2. Gate reaction if the part is eligible for it
32+
3. Falls back to "none"
33+
34+
## 4. Combo evaluation stat caching
35+
36+
**Design**: Mentions "cache `getTeamStats` per unique on-field character" but doesn't specify the caching strategy.
37+
38+
**Implementation**: `evaluateCombo()` uses a `Map<string, Record<string, StatSheet>>` keyed by on-field character ID. This works because `getTeamStats()` output depends only on the calc target (on-field character), not the formula being evaluated.
39+
40+
## 5. Arlecchino burst `eligibleReactions`
41+
42+
**Design**: Only mentions normal combo hits 0 and 3 as reaction-eligible for Arlecchino.
43+
44+
**Implementation**: Also added `eligibleReactions: ["vaporize", "melt"]` to the burst formula part, since the burst is a single hit that can always be amplified (no ICD concern). The original code had separate `arlecchino-burst-melt` and `arlecchino-burst-vape` entries confirming this.
45+
46+
## 6. Optimizer integration deferred
47+
48+
**Design**: Proposes using combo total as the optimization objective when combo mode is active.
49+
50+
**Implementation**: The optimizer still uses single-formula mode. The UI provides a mode toggle (Single Formula / Combo) for viewing, but optimization targets the selected single formula. Combo-based optimization is left for a future iteration since it requires changes to the multi-pass optimizer's marginal gain calculations and could affect optimization performance significantly.

src/components/account-data/ArtifactDataHoverCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ export function ArtifactDataHoverCard({
393393
<HoverCardContent
394394
ref={contentRef}
395395
side={side}
396-
className="w-auto p-0 border-none bg-transparent"
396+
className="w-auto p-0 border-none bg-slate-900 shadow-xl"
397397
>
398398
<ArtifactDataContent artifact={artifact} slot={slot} />
399399
</HoverCardContent>

src/components/account-data/BuildEvaluationCard.tsx

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ArtifactDataHoverCard } from "@/components/account-data/ArtifactDataHoverCard";
12
import {
23
Tooltip,
34
TooltipContent,
@@ -172,48 +173,33 @@ function BuildEvaluationCardComponent({
172173
return (
173174
<div key={slot} className="flex flex-col items-center gap-0.5">
174175
{hasArtifact ? (
175-
<Tooltip>
176-
<TooltipTrigger>
177-
<div className="relative">
178-
<img
179-
src={
180-
artifactsById[slotEval.artifact!.setKey]?.imagePaths[
181-
slot
182-
] || ""
183-
}
184-
alt=""
185-
className={cn(
186-
"w-11 h-11 2xl:w-14 2xl:h-14 rounded object-cover",
187-
isOffSet && "ring-1 ring-amber-500/60"
188-
)}
189-
/>
190-
{isOffSet && (
191-
<div className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 2xl:w-4 2xl:h-4 rounded-full bg-amber-500 flex items-center justify-center">
192-
<span className="text-[7px] 2xl:text-[8px] font-bold text-black">
193-
F
194-
</span>
195-
</div>
176+
<ArtifactDataHoverCard
177+
artifact={slotEval.artifact!}
178+
slot={slot}
179+
side="top"
180+
>
181+
<div className="relative">
182+
<img
183+
src={
184+
artifactsById[slotEval.artifact!.setKey]?.imagePaths[
185+
slot
186+
] || ""
187+
}
188+
alt=""
189+
className={cn(
190+
"w-11 h-11 2xl:w-14 2xl:h-14 rounded object-cover",
191+
isOffSet && "ring-1 ring-amber-500/60"
196192
)}
197-
</div>
198-
</TooltipTrigger>
199-
<TooltipContent
200-
side="top"
201-
className="text-xs max-w-[200px] bg-card/90 backdrop-blur-sm"
202-
>
203-
<div>
204-
{t.artifact(slotEval.artifact!.setKey)}
205-
{isOffSet && ` (${t.ui("evaluation.flex")})`}
206-
</div>
207-
<div className="text-muted-foreground">
208-
{t.stat(slotEval.artifact!.mainStatKey)} | +
209-
{slotEval.artifact!.level}
210-
</div>
211-
<div className="text-muted-foreground">
212-
{t.ui("evaluation.score")}: {slotEval.score.toFixed(1)} /{" "}
213-
{slotEval.maxScore.toFixed(1)}
214-
</div>
215-
</TooltipContent>
216-
</Tooltip>
193+
/>
194+
{isOffSet && (
195+
<div className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 2xl:w-4 2xl:h-4 rounded-full bg-amber-500 flex items-center justify-center">
196+
<span className="text-[7px] 2xl:text-[8px] font-bold text-black">
197+
F
198+
</span>
199+
</div>
200+
)}
201+
</div>
202+
</ArtifactDataHoverCard>
217203
) : (
218204
<div className="w-11 h-11 2xl:w-14 2xl:h-14 rounded border border-dashed border-muted-foreground/15 flex items-center justify-center">
219205
<span className="text-xs text-muted-foreground/20">--</span>

0 commit comments

Comments
 (0)