"use client"; // 回响星核 / Echo Nexus — 游戏主入口 import { useState, useEffect, useRef } from "react"; import { StarfieldCanvas } from "@/components/game/StarfieldCanvas"; import { ResourceBar } from "@/components/game/ResourceBar"; import { CrystalOrb } from "@/components/game/CrystalOrb"; import { DecodePanel } from "@/components/game/DecodeArray"; import { TechTree } from "@/components/game/TechTree"; 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 { ConstellationPanel } from "@/components/game/ConstellationPanel"; import { ConstellationDialog } from "@/components/game/ConstellationDialog"; import { ChronicleDialog } from "@/components/game/ChronicleDialog"; import { BeaconPanel } from "@/components/game/BeaconPanel"; import { TutorialOverlay } from "@/components/game/TutorialOverlay"; import { OfflineReportDialog } from "@/components/game/OfflineReportDialog"; import { CruiseMode } from "@/components/game/CruiseMode"; import { AttributesPanel } from "@/components/game/AttributesPanel"; import { IdleOperationsPanel } from "@/components/game/IdleOperationsPanel"; import { IdleStatusBadge } from "@/components/game/IdleStatusBadge"; import { IdleProjectBar } from "@/components/game/IdleProjectBar"; import { StatsPanel } from "@/components/game/StatsPanel"; import { StarTideNotifier, StarTideIndicator, StarTideOverlay, } from "@/components/game/StarTide"; 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"; import { Settings, RotateCcw, Sparkles, Cpu, BookOpen, BarChart3, Rocket, Github, Trophy, Star, Radio, Navigation, User, Clock, } from "lucide-react"; import { FRAGMENTS, formatNum } from "@/lib/game/config"; import { ACHIEVEMENTS } from "@/lib/game/achievements"; import { TIDE_EVENTS } from "@/lib/game/starTide"; import { generateDailyChallenge, loadDailyProgress } from "@/lib/game/beacon"; import { getUnlockedIdleProjects } from "@/lib/game/idle"; export default function Page() { useGameLoop(); useAudioSync(); const [prestigeOpen, setPrestigeOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); const [constellationOpen, setConstellationOpen] = useState(false); const [chronicleOpen, setChronicleOpen] = useState(false); const [cruiseOpen, setCruiseOpen] = useState(false); const [mounted, setMounted] = useState(false); const contact = useGameStore((s) => s.contact); const totalDecoded = useGameStore((s) => s.totalDecoded); const crystalsPerSec = useGameStore((s) => s.crystalsPerSec); 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 ownedConstellation = useGameStore((s) => s.constellation ?? []); const pendingPerkChoices = useGameStore((s) => s.pendingPerkChoices); const crystals = useGameStore((s) => s.crystals); const crystalCap = useGameStore((s) => s.crystalCap); const activeTide = useGameStore((s) => s.activeTide); const energy = useGameStore((s) => s.energy); const hasActiveExpedition = useGameStore((s) => !!s.activeExpedition && !s.activeExpedition.finished); const chronicleCount = useGameStore((s) => s.chronicle?.length ?? 0); const pendingAttrPoints = useGameStore((s) => s.pendingAttrPoints ?? 0); // v0.14 放置系统:用于驱动 goal 提示 + 标签默认值 const idleProjectSlots = useGameStore( (s) => (s.idleProjectSlots ?? [null, null, null]) as ( | { projectId: string; completed: boolean } | null )[] ); const createdAt = useGameStore((s) => s.createdAt); // 深空信标:检测是否有可领取的奖励(独立 localStorage) const [beaconClaimable, setBeaconClaimable] = useState(false); useEffect(() => { let cancelled = false; const check = () => { try { const c = generateDailyChallenge(); const p = loadDailyProgress(); if (!cancelled) { setBeaconClaimable(p.completedAt !== null && !p.claimed && p.dateKey === c.dateKey); } } catch { if (!cancelled) setBeaconClaimable(false); } }; check(); const id = setInterval(check, 2000); return () => { cancelled = true; clearInterval(id); }; }, []); // 挂载检测:避免持久化 store 在 SSR/CSR 间产生 hydration 不一致 useEffect(() => { setMounted(true); }, []); // v0.14 放置系统:默认放置标签(在挂载后按优先级切换一次) // 注意:所有 hooks 必须在早期 return 之前调用,保持调用顺序一致 const [activeTab, setActiveTab] = useState("idle"); const tabInited = useRef(false); const hasPendingPerkEarly = !!(pendingPerkChoices && pendingPerkChoices.length > 0); useEffect(() => { if (tabInited.current) return; tabInited.current = true; if (hasActiveExpedition) { setActiveTab("expedition"); } else if (hasPendingPerkEarly) { setActiveTab("constellation"); } else { setActiveTab("idle"); } }, [hasActiveExpedition, hasPendingPerkEarly]); // 防止 SSR/CSR 不一致 if (!mounted) { return (
ECHO NEXUS · v0.15