fix: use actual meditationMultiplier and baseRegen in ElementStatsSection; add cost breakdown per spec §11
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s

This commit is contained in:
2026-06-08 14:08:31 +02:00
parent b4b499c1b1
commit 9200cf3ce0
4 changed files with 61 additions and 7 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Circular Dependencies # Circular Dependencies
Generated: 2026-06-08T11:14:13.243Z Generated: 2026-06-08T11:29:45.994Z
Found: 1 circular chain(s) — these MUST be fixed before modifying involved files. Found: 1 circular chain(s) — these MUST be fixed before modifying involved files.
1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts 1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"_meta": { "_meta": {
"generated": "2026-06-08T11:14:11.296Z", "generated": "2026-06-08T11:29:44.022Z",
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
}, },
+2
View File
@@ -51,6 +51,8 @@ export function StatsTab() {
/> />
<ElementStatsSection <ElementStatsSection
elemMax={elemMax} elemMax={elemMax}
meditationMultiplier={manaStats.meditationMultiplier}
baseRegen={manaStats.baseRegen}
/> />
<LoopStatsSection /> <LoopStatsSection />
</div> </div>
@@ -15,9 +15,11 @@ import type { ElementState } from '@/lib/game/types';
interface ElementStatsSectionProps { interface ElementStatsSectionProps {
elemMax: number; elemMax: number;
meditationMultiplier: number;
baseRegen: number;
} }
export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) { export function ElementStatsSection({ elemMax, meditationMultiplier, baseRegen }: ElementStatsSectionProps) {
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
const signedPacts = usePrestigeStore((s) => s.signedPacts); const signedPacts = usePrestigeStore((s) => s.signedPacts);
const elements = useManaStore((s) => s.elements); const elements = useManaStore((s) => s.elements);
@@ -48,9 +50,9 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) {
signedPacts, signedPacts,
pactElementMap, pactElementMap,
invokerLevel, invokerLevel,
meditationMultiplier: 1, meditationMultiplier,
grossRegen, grossRegen,
rawGrossRegen: 2, rawGrossRegen: baseRegen,
}); });
}, [disciplines, attunements, signedPacts]); }, [disciplines, attunements, signedPacts]);
@@ -123,7 +125,7 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) {
<div className="text-xs font-medium mb-2" style={{ color: 'var(--text-muted)' }}>Conversion Breakdown:</div> <div className="text-xs font-medium mb-2" style={{ color: 'var(--text-muted)' }}>Conversion Breakdown:</div>
<div className="space-y-2"> <div className="space-y-2">
{activeConversions.map((entry) => ( {activeConversions.map((entry) => (
<ConversionRow key={entry.element} entry={entry} /> <ConversionRow key={entry.element} entry={entry} elementDrain={conversionData.elementDrain} />
))} ))}
</div> </div>
</> </>
@@ -134,8 +136,31 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) {
); );
} }
function ConversionRow({ entry }: { entry: { element: string; distance: number; disciplineRate: number; attunementBase: number; pactBase: number; baseRate: number; attunementMult: number; pactMult: number; meditationMult: number; finalRate: number; paused: boolean; pauseReason: string | null } }) { function ConversionRow({ entry, elementDrain }: {
entry: {
element: string;
distance: number;
disciplineRate: number;
attunementBase: number;
pactBase: number;
baseRate: number;
attunementMult: number;
pactMult: number;
meditationMult: number;
finalRate: number;
paused: boolean;
pauseReason: string | null;
rawCost: number;
componentCosts: Record<string, number>;
};
elementDrain: Record<string, number>;
}) {
const def = ELEMENTS[entry.element]; const def = ELEMENTS[entry.element];
const effectiveRate = entry.paused ? 0 : entry.finalRate;
const rawDrain = effectiveRate * entry.rawCost;
const isComponentDrained = Object.keys(entry.componentCosts).length > 0 &&
Object.keys(elementDrain).length > 0;
return ( return (
<div className="p-2 rounded text-xs" style={{ border: `1px solid ${def?.color}20`, background: 'var(--bg-sunken)/30' }}> <div className="p-2 rounded text-xs" style={{ border: `1px solid ${def?.color}20`, background: 'var(--bg-sunken)/30' }}>
<div className="flex items-center gap-1 mb-1" style={{ color: def?.color }}> <div className="flex items-center gap-1 mb-1" style={{ color: def?.color }}>
@@ -144,6 +169,7 @@ function ConversionRow({ entry }: { entry: { element: string; distance: number;
<span style={{ color: 'var(--text-muted)' }}>(d={entry.distance})</span> <span style={{ color: 'var(--text-muted)' }}>(d={entry.distance})</span>
{entry.paused && <span style={{ color: 'var(--color-error)' }}> PAUSED</span>} {entry.paused && <span style={{ color: 'var(--color-error)' }}> PAUSED</span>}
</div> </div>
{/* Rate breakdown */}
<div className="grid grid-cols-2 gap-x-4 gap-y-0.5" style={{ color: 'var(--text-muted)' }}> <div className="grid grid-cols-2 gap-x-4 gap-y-0.5" style={{ color: 'var(--text-muted)' }}>
<div>Discipline: <span style={{ color: 'var(--color-success)' }}>+{fmtDec(entry.disciplineRate, 2)}/hr</span></div> <div>Discipline: <span style={{ color: 'var(--color-success)' }}>+{fmtDec(entry.disciplineRate, 2)}/hr</span></div>
<div>Attunement: <span style={{ color: 'var(--color-success)' }}>+{fmtDec(entry.attunementBase, 2)}/hr</span></div> <div>Attunement: <span style={{ color: 'var(--color-success)' }}>+{fmtDec(entry.attunementBase, 2)}/hr</span></div>
@@ -154,6 +180,32 @@ function ConversionRow({ entry }: { entry: { element: string; distance: number;
<div>Med mult: <span>×{fmtDec(entry.meditationMult, 2)}</span></div> <div>Med mult: <span>×{fmtDec(entry.meditationMult, 2)}</span></div>
<div>Final: <span style={{ color: entry.paused ? 'var(--color-error)' : 'var(--color-success)' }}>{entry.paused ? '0.00' : fmtDec(entry.finalRate, 2)}/hr</span></div> <div>Final: <span style={{ color: entry.paused ? 'var(--color-error)' : 'var(--color-success)' }}>{entry.paused ? '0.00' : fmtDec(entry.finalRate, 2)}/hr</span></div>
</div> </div>
{/* Cost breakdown (per spec §11) */}
{!entry.paused && effectiveRate > 0 && (
<div className="mt-1.5 pt-1.5 border-t border-[var(--border-subtle)]">
<div className="mb-0.5" style={{ color: 'var(--text-muted)' }}>Costs (deducted from regen):</div>
<div className="grid grid-cols-2 gap-x-4 gap-y-0.5" style={{ color: 'var(--text-muted)' }}>
<div>Raw: <span style={{ color: 'var(--color-warning)' }}>-{fmtDec(rawDrain, 2)} raw/hr</span> <span style={{ color: 'var(--text-muted)' }}>({entry.rawCost} × {fmtDec(effectiveRate, 2)})</span></div>
{Object.entries(entry.componentCosts).map(([comp, cost]) => {
const compDrain = effectiveRate * cost;
const compDef = ELEMENTS[comp];
return (
<div key={comp}>
<span style={{ color: compDef?.color }}>{compDef?.sym} {comp}:</span>{' '}
<span style={{ color: 'var(--color-warning)' }}>-{fmtDec(compDrain, 2)}/hr</span>{' '}
<span style={{ color: 'var(--text-muted)' }}>({cost} × {fmtDec(effectiveRate, 2)})</span>
</div>
);
})}
</div>
{/* Downstream drain (this element consumed by higher conversions) */}
{isComponentDrained && elementDrain[entry.element] > 0 && (
<div className="mt-1" style={{ color: 'var(--color-warning)' }}>
Drained by downstream: -{fmtDec(elementDrain[entry.element], 2)} {def?.name}/hr
</div>
)}
</div>
)}
{entry.paused && entry.pauseReason && ( {entry.paused && entry.pauseReason && (
<div className="mt-1" style={{ color: 'var(--color-error)' }}> {entry.pauseReason}</div> <div className="mt-1" style={{ color: 'var(--color-error)' }}> {entry.pauseReason}</div>
)} )}