98ab975fb9
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 4m28s
- Updated AGENTS.md to include store/ directory and clarify store architecture - Updated GAME_BRIEFING.md Code Architecture section with store/ and legacy store info - Updated skills.md with skill state management information - Includes other refactoring changes (store hooks, component updates, etc.)
269 lines
12 KiB
TypeScript
269 lines
12 KiB
TypeScript
'use client';
|
||
|
||
import { getTierMultiplier } from '@/lib/game/skill-evolution';
|
||
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
||
import { fmt, fmtDec } from '@/lib/game/stores';
|
||
import type { UnifiedEffects } from '@/lib/game/effects';
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import { Separator } from '@/components/ui/separator';
|
||
import { Droplet } from 'lucide-react';
|
||
|
||
// Modular stores
|
||
import { useSkillStore, usePrestigeStore, useManaStore } from '@/lib/game/stores';
|
||
|
||
export interface ManaStatsSectionProps {
|
||
upgradeEffects: UnifiedEffects;
|
||
maxMana: number;
|
||
baseRegen: number;
|
||
clickMana: number;
|
||
meditationMultiplier: number;
|
||
effectiveRegen: number;
|
||
incursionStrength: number;
|
||
manaCascadeBonus: number;
|
||
manaWaterfallBonus: number;
|
||
hasManaWaterfall: boolean;
|
||
hasFlowSurge: boolean;
|
||
hasManaOverflow: boolean;
|
||
hasEternalFlow: boolean;
|
||
}
|
||
|
||
export function ManaStatsSection({
|
||
upgradeEffects,
|
||
maxMana,
|
||
baseRegen,
|
||
clickMana,
|
||
meditationMultiplier,
|
||
effectiveRegen,
|
||
incursionStrength,
|
||
manaCascadeBonus,
|
||
manaWaterfallBonus,
|
||
hasManaWaterfall,
|
||
hasFlowSurge,
|
||
hasManaOverflow,
|
||
hasEternalFlow,
|
||
}: ManaStatsSectionProps) {
|
||
// Get state from modular stores
|
||
const skills = useSkillStore((s) => s.skills);
|
||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
||
const rawMana = useManaStore((s) => s.rawMana);
|
||
|
||
return (
|
||
<Card className="bg-gray-900/80 border-gray-700">
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-blue-400 game-panel-title text-xs flex items-center gap-2">
|
||
<Droplet className="w-4 h-4" />
|
||
Mana Stats
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Base Max Mana:</span>
|
||
<span className="text-gray-200">100</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Well Bonus:</span>
|
||
<span className="text-blue-300">
|
||
{(() => {
|
||
const mw = skillTiers?.manaWell || 1;
|
||
const tieredSkillId = mw > 1 ? `manaWell_t${mw}` : 'manaWell';
|
||
const level = skills[tieredSkillId] || skills.manaWell || 0;
|
||
const tierMult = getTierMultiplier(tieredSkillId);
|
||
return `+${fmt(level * 100 * tierMult)} (${level} lvl × 100 × ${tierMult}x tier)`;
|
||
})()}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Prestige Mana Well:</span>
|
||
<span className="text-blue-300">+{fmt((prestigeUpgrades.manaWell || 0) * 500)}</span>
|
||
</div>
|
||
{upgradeEffects.maxManaBonus > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-amber-400">Upgrade Mana Bonus:</span>
|
||
<span className="text-amber-300">+{fmt(upgradeEffects.maxManaBonus)}</span>
|
||
</div>
|
||
)}
|
||
{upgradeEffects.maxManaMultiplier > 1 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-amber-400">Upgrade Mana Multiplier:</span>
|
||
<span className="text-amber-300">×{fmtDec(upgradeEffects.maxManaMultiplier, 2)}</span>
|
||
</div>
|
||
)}
|
||
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2">
|
||
<span className="text-gray-300">Total Max Mana:</span>
|
||
<span className="text-blue-400">{fmt(maxMana)}</span>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Base Regen:</span>
|
||
<span className="text-gray-200">2/hr</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Flow Bonus:</span>
|
||
<span className="text-blue-300">
|
||
{(() => {
|
||
const mf = skillTiers?.manaFlow || 1;
|
||
const tieredSkillId = mf > 1 ? `manaFlow_t${mf}` : 'manaFlow';
|
||
const level = skills[tieredSkillId] || skills.manaFlow || 0;
|
||
const tierMult = getTierMultiplier(tieredSkillId);
|
||
return `+${fmtDec(level * 1 * tierMult)}/hr (${level} lvl × 1 × ${tierMult}x tier)`;
|
||
})()}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Spring Bonus:</span>
|
||
<span className="text-blue-300">+{(skills.manaSpring || 0) * 2}/hr</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Prestige Mana Flow:</span>
|
||
<span className="text-blue-300">+{fmtDec((prestigeUpgrades.manaFlow || 0) * 0.5)}/hr</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Temporal Echo:</span>
|
||
<span className="text-blue-300">×{fmtDec(1 + (prestigeUpgrades.temporalEcho || 0) * 0.1, 2)}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2">
|
||
<span className="text-gray-300">Base Regen:</span>
|
||
<span className="text-blue-400">{fmtDec(baseRegen, 2)}/hr</span>
|
||
</div>
|
||
{upgradeEffects.regenBonus > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-amber-400">Upgrade Regen Bonus:</span>
|
||
<span className="text-amber-300">+{fmtDec(upgradeEffects.regenBonus, 2)}/hr</span>
|
||
</div>
|
||
)}
|
||
{upgradeEffects.permanentRegenBonus > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-amber-400">Permanent Regen Bonus:</span>
|
||
<span className="text-amber-300">+{fmtDec(upgradeEffects.permanentRegenBonus, 2)}/hr</span>
|
||
</div>
|
||
)}
|
||
{upgradeEffects.regenMultiplier > 1 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-amber-400">Upgrade Regen Multiplier:</span>
|
||
<span className="text-amber-300">×{fmtDec(upgradeEffects.regenMultiplier, 2)}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<Separator className="bg-gray-700 my-3" />
|
||
{upgradeEffects.activeUpgrades.length > 0 && (
|
||
<>
|
||
<div className="mb-2">
|
||
<span className="text-xs text-amber-400 game-panel-title">Active Skill Upgrades</span>
|
||
</div>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 mb-3">
|
||
{upgradeEffects.activeUpgrades.map((upgrade, idx) => (
|
||
<div key={idx} className="flex justify-between text-xs bg-gray-800/50 rounded px-2 py-1">
|
||
<span className="text-gray-300">{upgrade.name}</span>
|
||
<span className="text-gray-400">{upgrade.desc}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<Separator className="bg-gray-700 my-3" />
|
||
</>
|
||
)}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Click Mana Value:</span>
|
||
<span className="text-purple-300">+{clickMana}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Tap Bonus:</span>
|
||
<span className="text-purple-300">+{skills.manaTap || 0}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Surge Bonus:</span>
|
||
<span className="text-purple-300">+{(skills.manaSurge || 0) * 3}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-400">Mana Overflow:</span>
|
||
<span className="text-purple-300">×{fmtDec(1 + (skills.manaOverflow || 0) * 0.25, 2)}</span>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between text-sm">
|
||
<span className={`font-semibold ${meditationMultiplier > 1.5 ? 'text-purple-400' : 'text-gray-300'}`}>
|
||
Meditation Multiplier:
|
||
</span>
|
||
<span className={`font-semibold ${meditationMultiplier > 1.5 ? 'text-purple-400' : 'text-gray-300'}`}>
|
||
{fmtDec(meditationMultiplier, 2)}x
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-300">Effective Regen:</span>
|
||
<span className="text-green-400 font-semibold">{fmtDec(effectiveRegen, 2)}/hr</span>
|
||
</div>
|
||
{incursionStrength > 0 && !hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-red-400">Incursion Penalty:</span>
|
||
<span className="text-red-400">-{Math.round(incursionStrength * 100)}%</span>
|
||
</div>
|
||
)}
|
||
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && incursionStrength > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-green-400">Steady Stream:</span>
|
||
<span className="text-green-400">Immune to incursion</span>
|
||
</div>
|
||
)}
|
||
{manaCascadeBonus > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Mana Cascade Bonus:</span>
|
||
<span className="text-cyan-400">+{fmtDec(manaCascadeBonus, 2)}/hr</span>
|
||
</div>
|
||
)}
|
||
{manaWaterfallBonus > 0 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Mana Waterfall Bonus:</span>
|
||
<span className="text-cyan-400">+{fmtDec(manaWaterfallBonus, 2)}/hr</span>
|
||
</div>
|
||
)}
|
||
{hasManaWaterfall && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Mana Waterfall:</span>
|
||
<span className="text-cyan-400">+0.25 regen per 100 max mana</span>
|
||
</div>
|
||
)}
|
||
{hasFlowSurge && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Flow Surge:</span>
|
||
<span className="text-cyan-400">Clicks activate +100% regen for 1hr</span>
|
||
</div>
|
||
)}
|
||
{hasManaOverflow && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Mana Overflow:</span>
|
||
<span className="text-cyan-400">Raw mana can exceed max by 20%</span>
|
||
</div>
|
||
)}
|
||
{hasEternalFlow && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-green-400">Eternal Flow:</span>
|
||
<span className="text-green-400">Regen immune to ALL penalties</span>
|
||
</div>
|
||
)}
|
||
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_TORRENT) && rawMana > maxMana * 0.75 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Mana Torrent:</span>
|
||
<span className="text-cyan-400">+50% regen (high mana)</span>
|
||
</div>
|
||
)}
|
||
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.DESPERATE_WELLS) && rawMana < maxMana * 0.25 && (
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-cyan-400">Desperate Wells:</span>
|
||
<span className="text-cyan-400">+50% regen (low mana)</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
}
|
||
|
||
ManaStatsSection.displayName = "ManaStatsSection";
|