3d8566bc-063f-41e8-8934-68c9ac5b8211

This commit is contained in:
2026-06-23 13:38:23 +00:00
parent 674775be0e
commit da18f32df2
36 changed files with 849 additions and 34 deletions
+30 -7
View File
@@ -10,7 +10,10 @@ import { Codex } from "@/components/game/Codex";
import { PrestigeDialog } from "@/components/game/PrestigeDialog";
import { SettingsDialog } from "@/components/game/SettingsDialog";
import { ExpeditionPanel } from "@/components/game/ExpeditionPanel";
import { AchievementsPanel } from "@/components/game/AchievementsPanel";
import { AchievementNotifier } from "@/components/game/AchievementNotifier";
import { useGameLoop } from "@/hooks/useGameLoop";
import { useAudioSync } from "@/hooks/useAudio";
import { useGameStore } from "@/store/gameStore";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
@@ -23,11 +26,14 @@ import {
BarChart3,
Rocket,
Github,
Trophy,
} from "lucide-react";
import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config";
import { ACHIEVEMENTS } from "@/lib/game/achievements";
export default function Page() {
useGameLoop();
useAudioSync();
const [prestigeOpen, setPrestigeOpen] = useState(false);
const [settingsOpen, setSettingsOpen] = useState(false);
const [mounted, setMounted] = useState(false);
@@ -38,6 +44,7 @@ export default function Page() {
const ascensions = useGameStore((s) => s.ascensions);
const ownedTech = useGameStore((s) => s.tech);
const ownedFragments = useGameStore((s) => s.fragments);
const ownedAchievements = useGameStore((s) => s.achievements);
const crystals = useGameStore((s) => s.crystals);
const crystalCap = useGameStore((s) => s.crystalCap);
const energy = useGameStore((s) => s.energy);
@@ -60,6 +67,7 @@ export default function Page() {
const ownedTechCount = Object.values(ownedTech).filter((v) => v > 0).length;
const ownedFragCount = Object.values(ownedFragments).filter(Boolean).length;
const ownedAchCount = Object.values(ownedAchievements).filter(Boolean).length;
const canPrestige = contact >= 100;
// 仓库满仓警告
@@ -100,7 +108,7 @@ export default function Page() {
<div className="leading-tight">
<h1 className="text-base sm:text-lg font-bold text-gradient"></h1>
<p className="text-[9px] sm:text-[10px] text-muted-foreground/70 -mt-0.5 tracking-wider">
ECHO NEXUS · v0.1
ECHO NEXUS · v0.2.1
</p>
</div>
</div>
@@ -147,11 +155,11 @@ export default function Page() {
<div className="glass rounded-2xl p-4 flex-1 min-h-[360px] max-h-[560px]">
<DecodePanel />
</div>
{/* 标签面板:探险 / 技术 / 图谱 / 统计 */}
{/* 标签面板:探险 / 技术 / 图谱 / 成就 / 统计 */}
<div className="glass rounded-2xl p-3 min-h-[320px] max-h-[440px]">
<Tabs defaultValue={hasActiveExpedition ? "expedition" : "tech"} className="h-full flex flex-col">
<TabsList className="grid grid-cols-4 h-8 bg-black/30">
<TabsTrigger value="expedition" className="text-xs gap-1 relative">
<TabsList className="grid grid-cols-5 h-8 bg-black/30">
<TabsTrigger value="expedition" className="text-[11px] gap-0.5 relative px-1">
<Rocket className="h-3 w-3" />
{energy >= 1 && !hasActiveExpedition && (
@@ -161,15 +169,22 @@ export default function Page() {
<span className="absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-rose-400 animate-pulse" />
)}
</TabsTrigger>
<TabsTrigger value="tech" className="text-xs gap-1">
<TabsTrigger value="tech" className="text-[11px] gap-0.5 px-1">
<Cpu className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="codex" className="text-xs gap-1">
<TabsTrigger value="codex" className="text-[11px] gap-0.5 px-1">
<BookOpen className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="stats" className="text-xs gap-1">
<TabsTrigger value="ach" className="text-[11px] gap-0.5 relative px-1">
<Trophy className="h-3 w-3" />
{ownedAchCount < ACHIEVEMENTS.length && (
<span className="absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-amber-400 animate-pulse" />
)}
</TabsTrigger>
<TabsTrigger value="stats" className="text-[11px] gap-0.5 px-1">
<BarChart3 className="h-3 w-3" />
</TabsTrigger>
@@ -183,6 +198,9 @@ export default function Page() {
<TabsContent value="codex" className="flex-1 mt-2 min-h-0 data-[state=active]:flex data-[state=active]:flex-col">
<Codex />
</TabsContent>
<TabsContent value="ach" className="flex-1 mt-2 min-h-0 data-[state=active]:flex data-[state=active]:flex-col">
<AchievementsPanel />
</TabsContent>
<TabsContent value="stats" className="flex-1 mt-2 min-h-0">
<StatsPanel />
</TabsContent>
@@ -215,22 +233,27 @@ export default function Page() {
<PrestigeDialog open={prestigeOpen} onOpenChange={setPrestigeOpen} />
<SettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} />
<AchievementNotifier />
</div>
);
}
function StatsPanel() {
const s = useGameStore();
const achCount = Object.values(s.achievements).filter(Boolean).length;
const rows = [
{ label: "累计解码晶体", value: `${s.totalDecoded}` },
{ label: "飞升周目", value: `${s.ascensions}` },
{ label: "持有蓝图", value: `${s.blueprints.length} / 6` },
{ label: "已学技术", value: `${Object.values(s.tech).filter((v) => v > 0).length} / ${TECH_TREE.length}` },
{ label: "已获碎片", value: `${Object.values(s.fragments).filter(Boolean).length} / ${FRAGMENTS.length}` },
{ label: "已解锁成就", value: `${achCount} / ${ACHIEVEMENTS.length}` },
{ label: "接触进度", value: `${s.contact.toFixed(1)}%` },
{ label: "晶体产能", value: `${s.crystalsPerSec.toFixed(2)} /s` },
{ label: "仓库上限", value: `${formatNum(s.crystalCap)}` },
{ label: "洞见倍率", value: `×${s.insightMult.toFixed(2)}` },
{ label: "累计探险", value: `${s.totalExpeditions}` },
{ label: "探险能量", value: `${Math.floor(s.energy)} / ${s.energyMax}` },
];
return (
<div className="grid grid-cols-2 gap-1.5 text-xs">