From 23fb451a0b172ae61303a092c4ea230d9b2292f7 Mon Sep 17 00:00:00 2001 From: Super_Z <1401203083@qq.com> Date: Wed, 24 Jun 2026 01:05:43 +0000 Subject: [PATCH] 3cf83ed6-dca1-44be-bf7a-a27d19337739 --- src/app/page.tsx | 68 +- src/components/game/AchievementsPanel.tsx | 168 +++-- src/components/game/ExpeditionPanel.tsx | 19 +- src/components/game/ExplorationRadar.tsx | 215 +++++++ src/components/game/RelicCodex.tsx | 219 +++++++ src/lib/game/relicCodex.ts | 260 ++++++++ src/store/gameStore.ts | 10 +- .../read_1782262265760_479b0e593f07.txt | 609 ++++++++++++++++++ .../read_1782262268678_852a10fd13a6.txt | 609 ++++++++++++++++++ worklog.md | 73 +++ 10 files changed, 2157 insertions(+), 93 deletions(-) create mode 100644 src/components/game/ExplorationRadar.tsx create mode 100644 src/components/game/RelicCodex.tsx create mode 100644 src/lib/game/relicCodex.ts create mode 100644 tool-results/read_1782262265760_479b0e593f07.txt create mode 100644 tool-results/read_1782262268678_852a10fd13a6.txt diff --git a/src/app/page.tsx b/src/app/page.tsx index a9ce03f40..346069c22 100755 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -20,6 +20,8 @@ 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 { ExplorationRadar } from "@/components/game/ExplorationRadar"; +import { RelicCodex } from "@/components/game/RelicCodex"; import { StarTideNotifier, StarTideIndicator, @@ -49,6 +51,7 @@ import { Zap, Flame, Database, + BookMarked, } from "lucide-react"; import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config"; import { ACHIEVEMENTS } from "@/lib/game/achievements"; @@ -180,7 +183,7 @@ export default function Page() {

回响星核

- ECHO NEXUS · v0.8 + ECHO NEXUS · v0.10

@@ -249,52 +252,52 @@ export default function Page() { setPrestigeOpen(true)} /> - {/* 主体 — 小屏允许自然滚动,避免 min-h 总和超过视口导致挤压重叠 */} + {/* 主体 — v0.10 工单 #2 布局重构:反转左右比例,左栏紧凑晶体球,右栏扩展核心玩法 */}
-
- {/* 左侧:脉冲晶体 */} -
+
+ {/* 左侧:脉冲晶体(紧凑模式) + 收益 + 探索雷达 */} +
{/* 装饰光圈 */} -
-
- {/* v0.8.2 装饰全息环(填充留白,提升视觉层次) */} -
-
-
+
+
+ {/* v0.10 装饰全息环(缩小适应紧凑布局) */} +
+
{/* 四角全息标记 */} -
-
-
-
+
+
+
+
{/* 顶部状态条 */} -
+
CRYSTAL CORE · ONLINE
- {/* 底部铭文 */} -
- echo · nexus · archaeology -
-
+ + {/* 晶体球区域(紧凑居中) */} +
- {/* v0.9 工单 #2:全息收益信息面板 — 让晶体球价值可视化 */} + + {/* v0.9 全息收益信息面板 */} + + {/* v0.10 探索目标雷达(新增)— 填充留白,让晶体球区有价值 */} +
- {/* 右侧:解码 + 标签面板 */} + {/* 右侧:解码 + 标签面板(v0.10 扩展,从 420px 扩到 1fr) */}
{/* 解码面板 */} -
+
- {/* 标签面板:探险 / 技术 / 星图 / 图谱 / 成就 / 信标 / 统计 / 角色 */} - {/* v0.9 工单修复:移除 max-h 硬限制,改用 min-h + flex 自适应,避免成就/技术树内容被挤压遮挡 */} -
+ {/* 标签面板:探险 / 技术 / 星图 / 图谱 / 成就 / 信标 / 统计 / 角色 / 遗迹图鉴 */} +
- {/* v0.9 工单修复:标签页响应式布局,小屏 4 列 2 行避免拥挤遮挡 */} - + {/* v0.10 标签页响应式布局,小屏 3 列 3 行避免拥挤遮挡,大屏 9 列 */} + 探险 @@ -345,6 +348,10 @@ export default function Page() { )} + + + 图鉴 + @@ -370,6 +377,9 @@ export default function Page() { + + +
diff --git a/src/components/game/AchievementsPanel.tsx b/src/components/game/AchievementsPanel.tsx index 8e4afebb2..fd3a1024d 100755 --- a/src/components/game/AchievementsPanel.tsx +++ b/src/components/game/AchievementsPanel.tsx @@ -1,30 +1,64 @@ "use client"; -// 回响星核 / Echo Nexus — 成就系统面板 +// 回响星核 / Echo Nexus — 成就系统面板(v0.10 工单 #2 重写:单列 + 分类过滤 + 防溢出) +import { useState, useMemo } from "react"; import { useGameStore } from "@/store/gameStore"; -import { ACHIEVEMENTS } from "@/lib/game/achievements"; -import { achievementBonuses } from "@/lib/game/achievements"; -import { Trophy, Lock } from "lucide-react"; +import { ACHIEVEMENTS, achievementBonuses } from "@/lib/game/achievements"; +import { Trophy, Lock, Sparkles, Filter } from "lucide-react"; + +type FilterKey = "all" | "unlocked" | "locked"; + +const FILTER_TABS: { key: FilterKey; label: string; color: string }[] = [ + { key: "all", label: "全部", color: "#a78bfa" }, + { key: "unlocked", label: "已解锁", color: "#34d399" }, + { key: "locked", label: "未解锁", color: "#fb7185" }, +]; export function AchievementsPanel() { const achievements = useGameStore((s) => s.achievements); + const [filter, setFilter] = useState("all"); + const unlockedCount = Object.values(achievements).filter(Boolean).length; const bonus = achievementBonuses(achievements); + const totalCount = ACHIEVEMENTS.length; + const unlockPct = totalCount > 0 ? (unlockedCount / totalCount) * 100 : 0; + + // 按筛选条件过滤成就列表 + const filtered = useMemo(() => { + return ACHIEVEMENTS.filter((a) => { + const unlocked = !!achievements[a.id]; + if (filter === "unlocked") return unlocked; + if (filter === "locked") return !unlocked; + return true; + }); + }, [achievements, filter]); return ( -
- {/* 顶部:进度 + 加成总览 */} -
-

+
+ {/* 顶部:标题 + 进度 */} +
+

成就

- - {unlockedCount} / {ACHIEVEMENTS.length} - +
+ {unlockedCount} + / + {totalCount} + ({unlockPct.toFixed(0)}%) +
+
+ + {/* 进度条 */} +
+
{/* 永久加成条 */} -
+
+ 永久加成 {bonus.crystalsPerSecPct > 0 && ( 产能 +{bonus.crystalsPerSecPct}% @@ -33,64 +67,106 @@ export function AchievementsPanel() { 洞见 +{bonus.insightPct}% )} {bonus.crystalsPerSecPct === 0 && bonus.insightPct === 0 && ( - 暂无(达成成就解锁) + 达成成就解锁永久加成 )}
- {/* 成就网格 — 移除固定 max-h,用 flex-1 min-h-0 自适应父容器,避免小屏挤压重叠 */} -
- {ACHIEVEMENTS.map((a) => { + {/* 分类过滤标签 */} +
+ + {FILTER_TABS.map((t) => { + const active = filter === t.key; + const count = + t.key === "all" + ? totalCount + : t.key === "unlocked" + ? unlockedCount + : totalCount - unlockedCount; + return ( + + ); + })} +
+ + {/* 成就列表 — 单列展示,每个卡片 min-h 防止内容溢出被裁切 */} +
+ {filtered.length === 0 && ( +
+ 该分类下暂无成就 +
+ )} + {filtered.map((a) => { const unlocked = !!achievements[a.id]; return (
{unlocked && (
)} -
- {/* 图标徽章 */} -
- {unlocked ? a.icon : } -
- {/* 文本 */} -
-
+ {unlocked ? a.icon : } +
+ {/* 文本 — 使用 flex-1 + min-w-0 防止文本撑爆容器 */} +
+
+ {a.name} -
-
- {a.desc} -
+ {unlocked && ( -
- ✦ {a.rewardText} -
+ + ✓ + )}
+
+ {a.desc} +
+ {unlocked && ( +
+ ✦ {a.rewardText} +
+ )}
); @@ -101,6 +177,12 @@ export function AchievementsPanel() { .echo-scroll::-webkit-scrollbar { width: 5px; } .echo-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.14); border-radius: 3px; } .echo-scroll::-webkit-scrollbar-track { background: transparent; } + .line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } `}
); diff --git a/src/components/game/ExpeditionPanel.tsx b/src/components/game/ExpeditionPanel.tsx index 2eeda14da..b09f57d66 100755 --- a/src/components/game/ExpeditionPanel.tsx +++ b/src/components/game/ExpeditionPanel.tsx @@ -49,7 +49,6 @@ export function ExpeditionPanel() { const resolveCurrentNode = useGameStore((s) => s.resolveCurrentNode); const advanceNode = useGameStore((s) => s.advanceNode); const abortExpedition = useGameStore((s) => s.abortExpedition); - const dismissExpedition = useGameStore((s) => s.dismissExpedition); const { toast } = useToast(); const [lastLog, setLastLog] = useState(null); const [now, setNow] = useState(() => Date.now()); @@ -109,10 +108,8 @@ export function ExpeditionPanel() { }; // ===== 无活跃探险:展示入口 ===== + // v0.10 工单 #2 修复:探险结束自动清空 activeExpedition,无需 justFinished 判断 if (!activeExpedition || activeExpedition.finished) { - // v0.9 工单修复:如果探险刚结束(finished 且有摘要),显示结算面板 - const justFinished = activeExpedition?.finished && lastExpeditionSummary && - Date.now() - lastExpeditionSummary.finishedAt < 60000; return (
@@ -123,7 +120,7 @@ export function ExpeditionPanel() { 已探索 {totalExpeditions} 次
- {/* v0.9 工单修复:上次探险结果摘要 */} + {/* v0.9 工单修复:上次探险结果摘要(不限时间,常驻展示) */} {lastExpeditionSummary && (
)} - - {/* v0.9 工单修复:刚结束的探险,显示"返回"按钮 */} - {justFinished && ( - - )}
); } diff --git a/src/components/game/ExplorationRadar.tsx b/src/components/game/ExplorationRadar.tsx new file mode 100644 index 000000000..5dc23f24e --- /dev/null +++ b/src/components/game/ExplorationRadar.tsx @@ -0,0 +1,215 @@ +"use client"; +// 回响星核 / Echo Nexus — 探索目标雷达(v0.10 新增) +// 动态展示当前可解锁/可探索的下一目标,让"等待"变"期待" +// 缓解工单 #2 "占地方没用" 反馈 — 晶体球下方提供持续探索动力 +import { useGameStore } from "@/store/gameStore"; +import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config"; +import { ACHIEVEMENTS } from "@/lib/game/achievements"; +import { CONSTELLATION_PERKS } from "@/lib/game/constellation"; +import { EXPEDITION_CONFIG } from "@/lib/game/expedition"; +import { + Target, + Cpu, + Trophy, + BookOpen, + Rocket, + Star, + TrendingDown, + CheckCircle2, + Sparkles, +} from "lucide-react"; + +interface TargetItem { + id: string; + icon: React.ComponentType<{ className?: string }>; + color: string; + label: string; + detail: string; + progress: number; // 0-100 + hint: string; +} + +export function ExplorationRadar() { + const s = useGameStore(); + + // ===== 计算各系统的下一个目标 ===== + const targets: TargetItem[] = []; + + // 1. 下一个技术节点(按洞见排序) + const nextTech = TECH_TREE.filter((t) => !s.tech[t.id]).sort((a, b) => a.cost - b.cost)[0]; + if (nextTech) { + const insightProgress = Math.min(100, (s.insights / nextTech.cost) * 100); + targets.push({ + id: "tech_" + nextTech.id, + icon: Cpu, + color: "#34d399", + label: nextTech.name, + detail: `需 ${nextTech.cost} 洞见 · ${nextTech.desc}`, + progress: insightProgress, + hint: s.insights >= nextTech.cost ? "可解锁" : `还差 ${nextTech.cost - s.insights} 洞见`, + }); + } + + // 2. 下一个成就(按达成难度近似) + const nextAch = ACHIEVEMENTS.find((a) => !s.achievements[a.id]); + if (nextAch) { + targets.push({ + id: "ach_" + nextAch.id, + icon: Trophy, + color: "#fbbf24", + label: nextAch.name, + detail: nextAch.desc, + progress: 30, // 成就进度难以精确计算,给固定视觉提示 + hint: nextAch.rewardText, + }); + } + + // 3. 下一个记忆碎片 + const nextFrag = FRAGMENTS.find((f) => !s.fragments[f.id]); + if (nextFrag) { + const ownedFrag = FRAGMENTS.filter((f) => s.fragments[f.id]).length; + targets.push({ + id: "frag_" + nextFrag.id, + icon: BookOpen, + color: "#e879f9", + label: nextFrag.title, + detail: "解码晶体有概率获取", + progress: (ownedFrag / FRAGMENTS.length) * 100, + hint: `图谱 ${ownedFrag}/${FRAGMENTS.length}`, + }); + } + + // 4. 探险能量 + if (s.energy < EXPEDITION_CONFIG.energyCost) { + targets.push({ + id: "expedition_energy", + icon: Rocket, + color: "#fb7185", + label: "探险能量恢复中", + detail: `满能量可深入遗迹`, + progress: (s.energy / EXPEDITION_CONFIG.energyCost) * 100, + hint: `${s.energy}/${EXPEDITION_CONFIG.energyCost}`, + }); + } + + // 5. 飞升进度(接触<100 时) + if (s.contact < 100) { + targets.push({ + id: "ascension", + icon: Star, + color: "#a78bfa", + label: "接触进度", + detail: "满 100 可飞升新周目", + progress: s.contact, + hint: `${s.contact.toFixed(1)}/100`, + }); + } + + // 6. 星图天赋(飞升后未选择) + const ownedConstellation = s.constellation ?? []; + if (s.pendingPerkChoices && s.pendingPerkChoices.length > 0) { + targets.push({ + id: "constellation_pending", + icon: Sparkles, + color: "#e879f9", + label: "星图觉醒待选择", + detail: "3 选 1 永久天赋", + progress: 100, + hint: "立即选择", + }); + } else if (ownedConstellation.length < CONSTELLATION_PERKS.length && s.ascensions > 0) { + targets.push({ + id: "constellation_next", + icon: Star, + color: "#e879f9", + label: "下一道星图天赋", + detail: `已觉醒 ${ownedConstellation.length}/${CONSTELLATION_PERKS.length}`, + progress: (ownedConstellation.length / CONSTELLATION_PERKS.length) * 100, + hint: "飞升后解锁", + }); + } + + // 取前 3 个最相关目标展示 + const topTargets = targets.slice(0, 3); + + if (topTargets.length === 0) { + return ( +
+ +

所有目标已达成

+

继续飞升开启新周目

+
+ ); + } + + return ( +
+ {/* 标题 */} +
+
+ + 探索目标 +
+ RADAR +
+ + {/* 目标列表 */} +
+ {topTargets.map((t) => { + const Icon = t.icon; + const isReady = t.progress >= 100; + return ( +
+
+ + + {t.label} + + + {t.hint} + +
+ {/* 进度条 */} +
+
+
+ {/* 详情 */} +

{t.detail}

+
+ ); + })} +
+ + {/* 底部提示 */} +
+ + 持续探索 · 解锁深空奥秘 +
+
+ ); +} diff --git a/src/components/game/RelicCodex.tsx b/src/components/game/RelicCodex.tsx new file mode 100644 index 000000000..aed7e3244 --- /dev/null +++ b/src/components/game/RelicCodex.tsx @@ -0,0 +1,219 @@ +"use client"; +// 回响星核 / Echo Nexus — 文明遗迹图鉴面板(v0.10 新增) +// 展示深空考古发现的远古文明遗迹,增加世界观深度与探索乐趣 +import { useState } from "react"; +import { useGameStore } from "@/store/gameStore"; +import { + RELIC_CODEX, + getUnlockedRelics, + getRelicProgress, + getRelicRewards, + type RelicEntry, +} from "@/lib/game/relicCodex"; +import { BookMarked, Lock, Sparkles, Clock, Users, ScrollText } from "lucide-react"; + +export function RelicCodex() { + const state = useGameStore(); + const [selected, setSelected] = useState(null); + + const unlockedRelics = getUnlockedRelics(state); + const progress = getRelicProgress(state); + const rewards = getRelicRewards(state); + const selectedRelic = selected ? RELIC_CODEX.find((r) => r.id === selected) : null; + + return ( +
+ {/* 顶部:标题 + 进度 */} +
+

+ + 遗迹图鉴 +

+
+ {unlockedRelics.length} + / + {RELIC_CODEX.length} + ({progress.toFixed(0)}%) +
+
+ + {/* 进度条 */} +
+
+
+ + {/* 累计奖励总览 */} +
+ + 考古收获 + {rewards.crystals > 0 && ( + +{rewards.crystals} 晶体 + )} + {rewards.insights > 0 && ( + +{rewards.insights} 洞见 + )} + {rewards.contact > 0 && ( + +{rewards.contact} 接触 + )} + {rewards.crystalsPerSecPct > 0 && ( + 产能 +{rewards.crystalsPerSecPct}% + )} + {unlockedRelics.length === 0 && ( + 持续探索解锁奖励 + )} +
+ + {/* 遗迹网格 — 2 列展示,每个卡片 min-h 防溢出 */} +
+
+ {RELIC_CODEX.map((relic) => { + const unlocked = !!state.achievements?.[`relic_${relic.id}`] || relic.unlock(state); + const isSelected = selected === relic.id; + return ( + + ); + })} +
+ + {/* 选中遗迹的详细故事 */} + {selectedRelic && ( +
+
+
+
+ + {selectedRelic.icon} + + + {selectedRelic.name} + +
+
+ + + {selectedRelic.race} + + + + {selectedRelic.era} + +
+

+ {selectedRelic.brief} +

+
+ +

+ {selectedRelic.story} +

+
+
+ + + {selectedRelic.rewardText} + +
+
+
+ )} +
+ + +
+ ); +} diff --git a/src/lib/game/relicCodex.ts b/src/lib/game/relicCodex.ts new file mode 100644 index 000000000..cedaad272 --- /dev/null +++ b/src/lib/game/relicCodex.ts @@ -0,0 +1,260 @@ +// 回响星核 / Echo Nexus — 文明遗迹图鉴(v0.10 新增) +// 深空考古世界观载体:8 大远古文明遗迹,每个承载独特故事碎片 +// 通过游戏进度逐步解锁,增加"探索未知"的乐趣 +import type { GameState } from "./types"; + +export interface RelicEntry { + id: string; + name: string; + /** 文明种族 */ + race: string; + /** 距今时代 */ + era: string; + /** 图标 emoji */ + icon: string; + /** 主题色(emerald/rose/amber/fuchsia 四色全息) */ + color: string; + /** 简短描述 */ + brief: string; + /** 完整故事碎片(解锁后展示) */ + story: string; + /** 解锁条件描述 */ + unlockHint: string; + /** 解锁判定 */ + unlock: (s: GameState) => boolean; + /** 解锁奖励 */ + reward: { + crystals?: number; + insights?: number; + contact?: number; + }; + rewardText: string; +} + +export const RELIC_CODEX: RelicEntry[] = [ + { + id: "relic_first_echo", + name: "初次回响", + race: "无名先驱", + era: "纪元前 · 未知", + icon: "✦", + color: "#34d399", + brief: "深空中第一缕被捕获的智慧信号。", + story: + "当自治无人机首次解码出这缕信号时,整个星核为之震颤。那不是噪声,而是某种古老文明留下的问候——跨越亿万年的回响,证明我们并不孤独。", + unlockHint: "发起第一次脉冲扫描", + unlock: (s) => s.totalDecoded >= 0 || s.crystals > 0, + reward: { crystals: 10 }, + rewardText: "+10 晶体", + }, + { + id: "relic_crystal_whisper", + name: "晶体低语", + race: "晶语族", + era: "第一纪元 · 晶石时代", + icon: "◈", + color: "#fb7185", + brief: "能够将记忆封存在晶体中的远古种族。", + story: + "晶语族相信,记忆是宇宙的本质。他们建造了巨大的晶体矩阵,将整个文明的历史封存在谐振频率中。当他们的恒星熄灭时,唯有这些晶体在虚空中持续低语。", + unlockHint: "累计解码 10 颗记忆晶体", + unlock: (s) => s.totalDecoded >= 10, + reward: { insights: 20, crystalsPerSecPct: 5 } as any, + rewardText: "+20 洞见 · 产能 +5%", + }, + { + id: "relic_void_ark", + name: "虚空方舟", + race: "渡厄者", + era: "第二纪元 · 大迁徙", + icon: "❖", + color: "#fbbf24", + brief: "为逃离超新星爆发而建造的星际方舟。", + story: + "渡厄者的母星在一夜之间步入死亡。他们用三百年建造了十二艘方舟,每一艘都载着十亿灵魂的数字意识。如今,只有这一艘的残骸漂浮在我们的考古坐标上。", + unlockHint: "完成 5 次遗迹探险", + unlock: (s) => s.totalExpeditions >= 5, + reward: { insights: 30 }, + rewardText: "+30 洞见", + }, + { + id: "relic_silent_court", + name: "寂寂王朝", + race: "静默王族", + era: "第三纪元 · 铁律纪", + icon: "♛", + color: "#e879f9", + brief: "以沉默为律法的神秘王朝。", + story: + "静默王族统治了三百个恒星系,却从未留下任何文字。他们的律法刻在引力波中,他们的诗歌写在脉冲星里。我们只能通过残余的引力涟漪,窥见这个王朝的辉煌与寂灭。", + unlockHint: "击败 3 个探险 BOSS", + unlock: (s) => (s.bossKills ?? 0) >= 3, + reward: { contact: 10 }, + rewardText: "+10 接触", + }, + { + id: "relic_starforge", + name: "锻星炉", + race: "铸星者", + era: "第四纪元 · 恒星工程", + icon: "✺", + color: "#34d399", + brief: "能够人工点燃恒星的工程文明。", + story: + "铸星者不满足于利用恒星能量,他们要创造恒星。他们建造了锻星炉——一种能在虚空中凝聚物质的巨型结构。然而,每一次点火都会消耗一整个星系的物质,最终他们自己也成为了燃料。", + unlockHint: "解锁 6 项技术", + unlock: (s) => Object.values(s.tech).filter((v) => v > 0).length >= 6, + reward: { insights: 50, crystals: 100 }, + rewardText: "+50 洞见 · +100 晶体", + }, + { + id: "relic_memory_weaver", + name: "忆织者", + race: "织忆族", + era: "第五纪元 · 意识网络", + icon: "✧", + color: "#fb7185", + brief: "将集体意识编织成网络的心灵文明。", + story: + "织忆族没有个体概念。他们的每一个『我』都是网络的一个节点,死亡只是断开连接。当整个网络陷入沉睡时,他们编织了最后一张记忆之网,等待后人唤醒。我们的解码,正是唤醒。", + unlockHint: "拼凑 4 块记忆碎片", + unlock: (s) => Object.values(s.fragments).filter(Boolean).length >= 4, + reward: { contact: 15 }, + rewardText: "+15 接触", + }, + { + id: "relic_eclipse_tribe", + name: "蚀相部落", + race: "蚀相族", + era: "第六纪元 · 阴影纪", + icon: "☀", + color: "#fbbf24", + brief: "栖身于食恒星阴影中的猎手文明。", + story: + "蚀相族敬畏阴影。他们在双星系统的食相瞬间狩猎,在光与暗的边界舞蹈。当一颗恒星被吞噬殆尽,他们便迁徙至下一对双星。他们的遗迹,永远隐藏在某个行星的阴影里。", + unlockHint: "亲历 3 种星潮事件", + unlock: (s) => (s.starTidesEncountered ?? []).length >= 3, + reward: { insights: 40 }, + rewardText: "+40 洞见", + }, + { + id: "relic_ascended", + name: "飞升者遗迹", + race: "前代飞升者", + era: "终末纪元 · 超越", + icon: "✦", + color: "#e879f9", + brief: "曾经的飞升者留下的最后讯息。", + story: + "在我们之前,已有无数文明触碰到飞升的临界点。他们留下了什么?不是技术,不是财富,而是一句跨越时空的低语:「飞升不是终点,而是新的开始。勇敢者,继续探索。」", + unlockHint: "完成第一次飞升", + unlock: (s) => s.ascensions >= 1, + reward: { insights: 100, contact: 25 }, + rewardText: "+100 洞见 · +25 接触", + }, + { + id: "relic_constellation", + name: "星图密钥", + race: "观星者议会", + era: "永恒 · 星图守护者", + icon: "✧", + color: "#34d399", + brief: "守护星图天赋秘密的远古议会。", + story: + "观星者议会不属于任何时代。他们存在于所有时代的星图中,守护着飞升的真正秘密:每一道星图天赋,都是前代飞升者留下的礼物。集齐所有天赋,便能窥见宇宙的真相。", + unlockHint: "觉醒 6 道星图天赋", + unlock: (s) => (s.constellation ?? []).length >= 6, + reward: { insights: 80 }, + rewardText: "+80 洞见", + }, + { + id: "relic_chronicle", + name: "编年史之心", + race: "史官族", + era: "全纪元 · 记录者", + icon: "❖", + color: "#fb7185", + brief: "记录所有纪元兴衰的永恒史官。", + story: + "史官族从不干涉,只记录。他们的身体是活体的编年史,每一个细胞都承载着一个纪元的记忆。当最后一个史官死去时,他将所有记忆注入了这颗编年史之心,等待后人继承。", + unlockHint: "积累 5 段编年史", + unlock: (s) => (s.chronicle ?? []).length >= 5, + reward: { insights: 60, contact: 20 }, + rewardText: "+60 洞见 · +20 接触", + }, + { + id: "relic_attribute", + name: "四维之证", + race: "原型人格", + era: "元始 · 自我觉醒", + icon: "◈", + color: "#fbbf24", + brief: "自我觉醒的四维人格原型。", + story: + "探索力、智慧、勇气、灵感——这四维属性并非凭空而来。它们源自一个远古的原型人格,那个第一次说出「我」的存在。我们的每一次成长,都是对那个原型的回响。", + unlockHint: "任一角色属性达到 30", + unlock: (s) => { + const a = s.attributes; + if (!a) return false; + return ( + a.exploration >= 30 || + a.wisdom >= 30 || + a.courage >= 30 || + a.inspiration >= 30 + ); + }, + reward: { insights: 50 }, + rewardText: "+50 洞见", + }, + { + id: "relic_cruise", + name: "深空航迹", + race: "远航者联盟", + era: "第七纪元 · 星海远航", + icon: "➤", + color: "#e879f9", + brief: "穿越星海寻找新家园的远航者。", + story: + "远航者联盟由十二个种族组成,他们共享一艘巨型母舰,在星海中寻找新的家园。他们留下了航迹图——每一道光痕都是一段旅程,每一个航点都是一个希望。我们的巡航,正是延续他们的航迹。", + unlockHint: "深空巡航累计通关 5 关", + unlock: (s) => { + try { + const data = localStorage.getItem("echo-nexus-cruise-v1"); + if (!data) return false; + const parsed = JSON.parse(data); + return (parsed.totalClears ?? 0) >= 5; + } catch { + return false; + } + }, + reward: { insights: 70, crystals: 200 }, + rewardText: "+70 洞见 · +200 晶体", + }, +]; + +/** 获取已解锁的遗迹列表 */ +export function getUnlockedRelics(s: GameState): RelicEntry[] { + return RELIC_CODEX.filter((r) => r.unlock(s)); +} + +/** 获取图鉴进度(0-100) */ +export function getRelicProgress(s: GameState): number { + const unlocked = getUnlockedRelics(s).length; + return (unlocked / RELIC_CODEX.length) * 100; +} + +/** 获取已解锁遗迹的奖励总和 */ +export function getRelicRewards(s: GameState) { + const unlocked = getUnlockedRelics(s); + let crystals = 0; + let insights = 0; + let contact = 0; + let crystalsPerSecPct = 0; + for (const r of unlocked) { + if (r.reward.crystals) crystals += r.reward.crystals; + if (r.reward.insights) insights += r.reward.insights; + if (r.reward.contact) contact += r.reward.contact; + if ((r.reward as any).crystalsPerSecPct) crystalsPerSecPct += (r.reward as any).crystalsPerSecPct; + } + return { crystals, insights, contact, crystalsPerSecPct }; +} diff --git a/src/store/gameStore.ts b/src/store/gameStore.ts index 4f8864459..9ea8fe9f7 100755 --- a/src/store/gameStore.ts +++ b/src/store/gameStore.ts @@ -925,8 +925,9 @@ export const useGameStore = create()( }; const newLog = [logEntry, ...s.expeditionLog].slice(0, 30); + // v0.10 工单 #2 修复:探险结束时立即清空 activeExpedition,避免 finished 状态卡死 + // 保留 lastExpeditionSummary 用于入口展示「上次结果」 if (result.ended) { - // 探险结束(胜利或失败) exp.finished = true; } @@ -988,7 +989,8 @@ export const useGameStore = create()( } set({ - activeExpedition: exp, + // v0.10 工单 #2 修复:探险结束立即清空 activeExpedition,让玩家无障碍再出发 + activeExpedition: result.ended ? null : exp, crystals: newCrystals, insights: newInsights, contact: newContact, @@ -1028,7 +1030,6 @@ export const useGameStore = create()( const s = get(); if (!s.activeExpedition) return; const exp = JSON.parse(JSON.stringify(s.activeExpedition)); - exp.finished = true; const logEntry = { expeditionId: exp.id, nodeType: "rest" as const, @@ -1044,7 +1045,8 @@ export const useGameStore = create()( finishedAt: Date.now(), }; set({ - activeExpedition: exp, + // v0.10 工单 #2 修复:撤退立即清空 activeExpedition,避免 finished 卡死 + activeExpedition: null, expeditionLog: [logEntry, ...s.expeditionLog].slice(0, 30), lastExpeditionSummary: summary, }); diff --git a/tool-results/read_1782262265760_479b0e593f07.txt b/tool-results/read_1782262265760_479b0e593f07.txt new file mode 100644 index 000000000..832a8234b --- /dev/null +++ b/tool-results/read_1782262265760_479b0e593f07.txt @@ -0,0 +1,609 @@ + 1→"use client"; + 2→// 回响星核 / Echo Nexus — 游戏主入口 + 3→import { useState, useEffect } from "react"; + 4→import { StarfieldCanvas } from "@/components/game/StarfieldCanvas"; + 5→import { ResourceBar } from "@/components/game/ResourceBar"; + 6→import { CrystalOrb } from "@/components/game/CrystalOrb"; + 7→import { DecodePanel } from "@/components/game/DecodeArray"; + 8→import { TechTree } from "@/components/game/TechTree"; + 9→import { Codex } from "@/components/game/Codex"; + 10→import { PrestigeDialog } from "@/components/game/PrestigeDialog"; + 11→import { SettingsDialog } from "@/components/game/SettingsDialog"; + 12→import { ExpeditionPanel } from "@/components/game/ExpeditionPanel"; + 13→import { AchievementsPanel } from "@/components/game/AchievementsPanel"; + 14→import { AchievementNotifier } from "@/components/game/AchievementNotifier"; + 15→import { ConstellationPanel } from "@/components/game/ConstellationPanel"; + 16→import { ConstellationDialog } from "@/components/game/ConstellationDialog"; + 17→import { ChronicleDialog } from "@/components/game/ChronicleDialog"; + 18→import { BeaconPanel } from "@/components/game/BeaconPanel"; + 19→import { TutorialOverlay } from "@/components/game/TutorialOverlay"; + 20→import { OfflineReportDialog } from "@/components/game/OfflineReportDialog"; + 21→import { CruiseMode } from "@/components/game/CruiseMode"; + 22→import { AttributesPanel } from "@/components/game/AttributesPanel"; + 23→import { + 24→ StarTideNotifier, + 25→ StarTideIndicator, + 26→ StarTideOverlay, + 27→} from "@/components/game/StarTide"; + 28→import { useGameLoop } from "@/hooks/useGameLoop"; + 29→import { useAudioSync } from "@/hooks/useAudio"; + 30→import { useGlobalTide } from "@/hooks/useGlobalTide"; + 31→import { useGameStore } from "@/store/gameStore"; + 32→import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + 33→import { Button } from "@/components/ui/button"; + 34→import { + 35→ Settings, + 36→ RotateCcw, + 37→ Sparkles, + 38→ Cpu, + 39→ BookOpen, + 40→ BarChart3, + 41→ Rocket, + 42→ Github, + 43→ Trophy, + 44→ Star, + 45→ Radio, + 46→ Navigation, + 47→ User, + 48→ Gem, + 49→ Zap, + 50→ Flame, + 51→ Database, + 52→} from "lucide-react"; + 53→import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config"; + 54→import { ACHIEVEMENTS } from "@/lib/game/achievements"; + 55→import { TIDE_EVENTS } from "@/lib/game/starTide"; + 56→import { CONSTELLATION_PERKS } from "@/lib/game/constellation"; + 57→import { generateDailyChallenge, loadDailyProgress, loadLeaderboard } from "@/lib/game/beacon"; + 58→ + 59→export default function Page() { + 60→ useGameLoop(); + 61→ useAudioSync(); + 62→ useGlobalTide(); + 63→ const [prestigeOpen, setPrestigeOpen] = useState(false); + 64→ const [settingsOpen, setSettingsOpen] = useState(false); + 65→ const [constellationOpen, setConstellationOpen] = useState(false); + 66→ const [chronicleOpen, setChronicleOpen] = useState(false); + 67→ const [cruiseOpen, setCruiseOpen] = useState(false); + 68→ const [mounted, setMounted] = useState(false); + 69→ + 70→ const contact = useGameStore((s) => s.contact); + 71→ const totalDecoded = useGameStore((s) => s.totalDecoded); + 72→ const crystalsPerSec = useGameStore((s) => s.crystalsPerSec); + 73→ const ascensions = useGameStore((s) => s.ascensions); + 74→ const ownedTech = useGameStore((s) => s.tech); + 75→ const ownedFragments = useGameStore((s) => s.fragments); + 76→ const ownedAchievements = useGameStore((s) => s.achievements); + 77→ const ownedConstellation = useGameStore((s) => s.constellation ?? []); + 78→ const pendingPerkChoices = useGameStore((s) => s.pendingPerkChoices); + 79→ const crystals = useGameStore((s) => s.crystals); + 80→ const crystalCap = useGameStore((s) => s.crystalCap); + 81→ const activeTide = useGameStore((s) => s.activeTide); + 82→ const globalTide = useGameStore((s) => s.globalTide); + 83→ const energy = useGameStore((s) => s.energy); + 84→ const hasActiveExpedition = useGameStore((s) => !!s.activeExpedition && !s.activeExpedition.finished); + 85→ const chronicleCount = useGameStore((s) => s.chronicle?.length ?? 0); + 86→ const pendingAttrPoints = useGameStore((s) => s.pendingAttrPoints ?? 0); + 87→ + 88→ // 深空信标:检测是否有可领取的奖励(独立 localStorage) + 89→ const [beaconClaimable, setBeaconClaimable] = useState(false); + 90→ useEffect(() => { + 91→ let cancelled = false; + 92→ const check = () => { + 93→ try { + 94→ const c = generateDailyChallenge(); + 95→ const p = loadDailyProgress(); + 96→ if (!cancelled) { + 97→ setBeaconClaimable(p.completedAt !== null && !p.claimed && p.dateKey === c.dateKey); + 98→ } + 99→ } catch { + 100→ if (!cancelled) setBeaconClaimable(false); + 101→ } + 102→ }; + 103→ check(); + 104→ const id = setInterval(check, 2000); + 105→ return () => { + 106→ cancelled = true; + 107→ clearInterval(id); + 108→ }; + 109→ }, []); + 110→ + 111→ // 挂载检测:避免持久化 store 在 SSR/CSR 间产生 hydration 不一致 + 112→ useEffect(() => { + 113→ // eslint-disable-next-line react-hooks/set-state-in-effect + 114→ setMounted(true); + 115→ }, []); + 116→ + 117→ // 防止 SSR/CSR 不一致 + 118→ if (!mounted) { + 119→ return ( + 120→
+ 121→
唤醒回响中…
+ 122→
+ 123→ ); + 124→ } + 125→ + 126→ const ownedTechCount = Object.values(ownedTech).filter((v) => v > 0).length; + 127→ const ownedFragCount = Object.values(ownedFragments).filter(Boolean).length; + 128→ const ownedAchCount = Object.values(ownedAchievements).filter(Boolean).length; + 129→ const canPrestige = contact >= 100; + 130→ const hasPendingPerk = pendingPerkChoices && pendingPerkChoices.length > 0; + 131→ + 132→ // 仓库满仓警告 + 133→ const warehouseFull = crystals >= crystalCap * 0.98; + 134→ + 135→ // 目标提示 + 136→ let goal = "点击中央晶体发起脉冲,累积记忆晶体"; + 137→ if (totalDecoded === 0 && crystals >= 5) { + 138→ goal = "右侧「待解码晶体」点击一颗晶体,开始解码"; + 139→ } else if (totalDecoded > 0 && ownedTechCount === 0) { + 140→ goal = "用洞见解锁技术树,提升产能"; + 141→ } else if (totalDecoded > 0 && ownedFragCount < FRAGMENTS.length) { + 142→ goal = `继续解码,拼凑记忆图谱(${ownedFragCount}/${FRAGMENTS.length})`; + 143→ } + 144→ if (energy >= 1 && totalDecoded >= 3) { + 145→ goal = "「探险」标签可深入遗迹,获取丰厚奖励"; + 146→ } + 147→ if (ascensions >= 1 && ownedConstellation.length > 0) { + 148→ goal = `星图天赋已觉醒 ${ownedConstellation.length}/18,继续飞升获取更多`; + 149→ } + 150→ if (warehouseFull) { + 151→ goal = "⚠ 仓库已满,产能浪费中!请解码晶体或升级仓库"; + 152→ } + 153→ if (hasPendingPerk) { + 154→ goal = "✦ 星图觉醒!点击顶部「星图」按钮选择一道天赋"; + 155→ } + 156→ if (activeTide) { + 157→ const tm = TIDE_EVENTS[activeTide.type]; + 158→ const prefix = globalTide ? "🌐 全球星潮 · " : ""; + 159→ goal = `${prefix}${tm.icon} 星潮「${tm.name}」进行中 · ${tm.desc}`; + 160→ } + 161→ if (canPrestige) goal = "✦ 接触进度已满,可发起飞升进入新周目"; + 162→ + 163→ return ( + 164→
+ 165→ {/* 星空背景 */} + 166→ + 167→ {/* 星潮背景叠层 */} + 168→ + 169→ + 170→ {/* 顶部 Header */} + 171→
+ 172→
+ 173→
+ 174→
+ 175→
+ 176→
+ 177→ + 178→
+ 179→
+ 180→
+ 181→

回响星核

+ 182→

+ 183→ ECHO NEXUS · v0.8 + 184→

+ 185→
+ 186→
+ 187→
+ 188→ + 189→ + 200→ + 215→ {hasPendingPerk && ( + 216→ + 225→ )} + 226→ {canPrestige && ( + 227→ + 237→ )} + 238→ + 247→
+ 248→
+ 249→ setPrestigeOpen(true)} /> + 250→
+ 251→ + 252→ {/* 主体 — 小屏允许自然滚动,避免 min-h 总和超过视口导致挤压重叠 */} + 253→
+ 254→
+ 255→ {/* 左侧:脉冲晶体 */} + 256→
+ 257→ {/* 装饰光圈 */} + 258→
+ 259→
+ 260→ {/* v0.8.2 装饰全息环(填充留白,提升视觉层次) */} + 261→
+ 262→
+ 263→
+ 264→ {/* 四角全息标记 */} + 265→
+ 266→
+ 267→
+ 268→
+ 269→ {/* 顶部状态条 */} + 270→
+ 271→ + 272→ CRYSTAL CORE · ONLINE + 273→ + 274→
+ 275→ {/* 底部铭文 */} + 276→
+ 277→ echo · nexus · archaeology + 278→
+ 279→
+ 280→ + 281→
+ 282→ {/* v0.9 工单 #2:全息收益信息面板 — 让晶体球价值可视化 */} + 283→ + 284→
+ 285→ + 286→ {/* 右侧:解码 + 标签面板 */} + 287→
+ 288→ {/* 解码面板 */} + 289→
+ 290→ + 291→
+ 292→ {/* 标签面板:探险 / 技术 / 星图 / 图谱 / 成就 / 信标 / 统计 / 角色 */} + 293→ {/* v0.9 工单修复:移除 max-h 硬限制,改用 min-h + flex 自适应,避免成就/技术树内容被挤压遮挡 */} + 294→
+ 295→ + 296→ {/* v0.9 工单修复:标签页响应式布局,小屏 4 列 2 行避免拥挤遮挡 */} + 297→ + 298→ + 299→ + 300→ 探险 + 301→ {energy >= 1 && !hasActiveExpedition && ( + 302→ + 303→ )} + 304→ {hasActiveExpedition && ( + 305→ + 306→ )} + 307→ + 308→ + 309→ + 310→ 技术 + 311→ + 312→ + 313→ + 314→ 星图 + 315→ {hasPendingPerk && ( + 316→ + 317→ )} + 318→ + 319→ + 320→ + 321→ 图谱 + 322→ + 323→ + 324→ + 325→ 成就 + 326→ {ownedAchCount < ACHIEVEMENTS.length && ( + 327→ + 328→ )} + 329→ + 330→ + 331→ + 332→ 信标 + 333→ {beaconClaimable && ( + 334→ + 335→ )} + 336→ + 337→ + 338→ + 339→ 统计 + 340→ + 341→ + 342→ + 343→ 角色 + 344→ {pendingAttrPoints > 0 && ( + 345→ + 346→ )} + 347→ + 348→ + 349→ + 350→ + 351→ + 352→ + 353→ + 354→ + 355→ + 356→ + 357→ + 358→ + 359→ + 360→ + 361→ + 362→ + 363→ + 364→ + 365→ + 366→ + 367→ + 368→ + 369→ + 370→ + 371→ + 372→ + 373→ + 374→
+ 375→
+ 376→
+ 377→
+ 378→ + 379→ {/* 底部 Footer */} + 380→
+ 381→
+ 391→ + 401→ ▶ + 402→ + 403→ + 408→ {goal} + 409→ + 410→ | + 411→ + 412→ 产能 {formatNum(crystalsPerSec)}/s + 413→ + 414→ + 421→ + 422→ + 423→
+ 424→
+ 425→ + 426→ + 427→ + 428→ + 429→ + 430→ + 431→ + 432→ + 433→ + 434→ {cruiseOpen && setCruiseOpen(false)} />} + 435→
+ 436→ ); + 437→} + 438→ + 439→/** + 440→ * v0.9 工单 #2:全息收益信息面板 + 441→ * 在中央晶体球下方展示 4 项核心收益指标(产能 / 脉冲 / 连击 / 仓库), + 442→ * 让玩家明确感知晶体球的价值,缓解"占地方又没用"的反馈。 + 443→ * 严格四色全息配色:emerald / rose / amber / fuchsia(禁止蓝色/靛色)。 + 444→ */ + 445→function CrystalYieldPanel() { + 446→ const crystalsPerSec = useGameStore((s) => s.crystalsPerSec); + 447→ const pulsePower = useGameStore((s) => s.pulsePower); + 448→ const combo = useGameStore((s) => s._combo); + 449→ const crystals = useGameStore((s) => s.crystals); + 450→ const crystalCap = useGameStore((s) => s.crystalCap); + 451→ + 452→ // 连击倍率:1 + (combo - 1) * 0.15(仅 combo≥1 时生效) + 453→ const comboMult = combo >= 1 ? 1 + (combo - 1) * 0.15 : 1; + 454→ const comboHot = combo >= 3; // 连击≥3 高亮闪烁 + 455→ + 456→ // 仓库容量百分比 + 457→ const fillPct = crystalCap > 0 ? (crystals / crystalCap) * 100 : 0; + 458→ const warehouseWarn = fillPct >= 90; // ≥90% 警告色 + pulse + 459→ + 460→ return ( + 461→
+ 462→ {/* 晶体产能 — emerald */} + 463→
+ 464→ + 465→
+ 466→
+ 467→ 晶体产能 + 468→
+ 469→
+ 470→ {formatNum(crystalsPerSec)} + 471→ /s + 472→
+ 473→
+ 474→
+ 475→ + 476→ {/* 主动脉冲 — rose */} + 477→
+ 478→ + 479→
+ 480→
+ 481→ 主动脉冲 + 482→
+ 483→
+ 484→ {formatNum(pulsePower)} + 485→ {combo >= 1 && ( + 486→ ×{comboMult.toFixed(2)} + 487→ )} + 488→
+ 489→
+ 490→
+ 491→ + 492→ {/* 当前连击 — amber,≥3 时高亮闪烁 */} + 493→
+ 498→ + 503→
+ 504→
+ 505→ 当前连击 + 506→
+ 507→
+ 512→ {combo} + 513→ /10 + 514→
+ 515→
+ 516→
+ 517→ + 518→ {/* 仓库容量 — fuchsia,≥90% 切 rose 警告色 + pulse */} + 519→
+ 524→ + 529→
+ 530→
+ 535→ 仓库容量 + 536→
+ 537→
+ 542→ {formatNum(crystals)} + 543→ + 548→ /{formatNum(crystalCap)} + 549→ + 550→
+ 551→
+ 552→
+ 553→
+ 554→ ); + 555→} + 556→ + 557→function StatsPanel() { + 558→ const s = useGameStore(); + 559→ const achCount = Object.values(s.achievements).filter(Boolean).length; + 560→ // 深空信标本地排行榜最高分(独立 localStorage) + 561→ const [beaconBest, setBeaconBest] = useState(null); + 562→ useEffect(() => { + 563→ try { + 564→ const lb = loadLeaderboard(); + 565→ // eslint-disable-next-line react-hooks/set-state-in-effect + 566→ setBeaconBest(lb.length > 0 ? lb[0].score : null); + 567→ } catch { + 568→ setBeaconBest(null); + 569→ } + 570→ }, []); + 571→ const rows = [ + 572→ { label: "累计解码晶体", value: `${s.totalDecoded} 颗` }, + 573→ { label: "飞升周目", value: `${s.ascensions}` }, + 574→ { label: "持有蓝图", value: `${s.blueprints.length} / 6` }, + 575→ { label: "已学技术", value: `${Object.values(s.tech).filter((v) => v > 0).length} / ${TECH_TREE.length}` }, + 576→ { label: "已获碎片", value: `${Object.values(s.fragments).filter(Boolean).length} / ${FRAGMENTS.length}` }, + 577→ { label: "已解锁成就", value: `${achCount} / ${ACHIEVEMENTS.length}` }, + 578→ { label: "星图天赋", value: `${s.constellation?.length ?? 0} / ${CONSTELLATION_PERKS.length}` }, + 579→ { label: "接触进度", value: `${s.contact.toFixed(1)}%` }, + 580→ { label: "晶体产能", value: `${s.crystalsPerSec.toFixed(2)} /s` }, + 581→ { label: "仓库上限", value: `${formatNum(s.crystalCap)}` }, + 582→ { label: "洞见倍率", value: `×${s.insightMult.toFixed(2)}` }, + 583→ { label: "累计探险", value: `${s.totalExpeditions} 次` }, + 584→ { label: "BOSS 击破", value: `${s.bossKills ?? 0} 次` }, + 585→ { label: "星潮亲历", value: `${(s.starTidesEncountered ?? []).length} / 9` }, + 586→ { label: "编年史条目", value: `${(s.chronicle ?? []).length} 纪元` }, + 587→ { label: "探险能量", value: `${Math.floor(s.energy)} / ${s.energyMax}` }, + 588→ { label: "信标最高分", value: beaconBest !== null ? formatNum(beaconBest) : "—" }, + 589→ { label: "探索力", value: `${s.attributes?.exploration ?? 0} / 100` }, + 590→ { label: "智慧", value: `${s.attributes?.wisdom ?? 0} / 100` }, + 591→ { label: "勇气", value: `${s.attributes?.courage ?? 0} / 100` }, + 592→ { label: "灵感", value: `${s.attributes?.inspiration ?? 0} / 100` }, + 593→ { label: "待分配属性点", value: `${s.pendingAttrPoints ?? 0}` }, + 594→ ]; + 595→ return ( + 596→
+ 597→ {rows.map((r) => ( + 598→
+ 602→ {r.label} + 603→ {r.value} + 604→
+ 605→ ))} + 606→
+ 607→ ); + 608→} + 609→ \ No newline at end of file diff --git a/tool-results/read_1782262268678_852a10fd13a6.txt b/tool-results/read_1782262268678_852a10fd13a6.txt new file mode 100644 index 000000000..2d2e172f8 --- /dev/null +++ b/tool-results/read_1782262268678_852a10fd13a6.txt @@ -0,0 +1,609 @@ + 1→ 1→"use client"; + 2→ 2→// 回响星核 / Echo Nexus — 游戏主入口 + 3→ 3→import { useState, useEffect } from "react"; + 4→ 4→import { StarfieldCanvas } from "@/components/game/StarfieldCanvas"; + 5→ 5→import { ResourceBar } from "@/components/game/ResourceBar"; + 6→ 6→import { CrystalOrb } from "@/components/game/CrystalOrb"; + 7→ 7→import { DecodePanel } from "@/components/game/DecodeArray"; + 8→ 8→import { TechTree } from "@/components/game/TechTree"; + 9→ 9→import { Codex } from "@/components/game/Codex"; + 10→ 10→import { PrestigeDialog } from "@/components/game/PrestigeDialog"; + 11→ 11→import { SettingsDialog } from "@/components/game/SettingsDialog"; + 12→ 12→import { ExpeditionPanel } from "@/components/game/ExpeditionPanel"; + 13→ 13→import { AchievementsPanel } from "@/components/game/AchievementsPanel"; + 14→ 14→import { AchievementNotifier } from "@/components/game/AchievementNotifier"; + 15→ 15→import { ConstellationPanel } from "@/components/game/ConstellationPanel"; + 16→ 16→import { ConstellationDialog } from "@/components/game/ConstellationDialog"; + 17→ 17→import { ChronicleDialog } from "@/components/game/ChronicleDialog"; + 18→ 18→import { BeaconPanel } from "@/components/game/BeaconPanel"; + 19→ 19→import { TutorialOverlay } from "@/components/game/TutorialOverlay"; + 20→ 20→import { OfflineReportDialog } from "@/components/game/OfflineReportDialog"; + 21→ 21→import { CruiseMode } from "@/components/game/CruiseMode"; + 22→ 22→import { AttributesPanel } from "@/components/game/AttributesPanel"; + 23→ 23→import { + 24→ 24→ StarTideNotifier, + 25→ 25→ StarTideIndicator, + 26→ 26→ StarTideOverlay, + 27→ 27→} from "@/components/game/StarTide"; + 28→ 28→import { useGameLoop } from "@/hooks/useGameLoop"; + 29→ 29→import { useAudioSync } from "@/hooks/useAudio"; + 30→ 30→import { useGlobalTide } from "@/hooks/useGlobalTide"; + 31→ 31→import { useGameStore } from "@/store/gameStore"; + 32→ 32→import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + 33→ 33→import { Button } from "@/components/ui/button"; + 34→ 34→import { + 35→ 35→ Settings, + 36→ 36→ RotateCcw, + 37→ 37→ Sparkles, + 38→ 38→ Cpu, + 39→ 39→ BookOpen, + 40→ 40→ BarChart3, + 41→ 41→ Rocket, + 42→ 42→ Github, + 43→ 43→ Trophy, + 44→ 44→ Star, + 45→ 45→ Radio, + 46→ 46→ Navigation, + 47→ 47→ User, + 48→ 48→ Gem, + 49→ 49→ Zap, + 50→ 50→ Flame, + 51→ 51→ Database, + 52→ 52→} from "lucide-react"; + 53→ 53→import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config"; + 54→ 54→import { ACHIEVEMENTS } from "@/lib/game/achievements"; + 55→ 55→import { TIDE_EVENTS } from "@/lib/game/starTide"; + 56→ 56→import { CONSTELLATION_PERKS } from "@/lib/game/constellation"; + 57→ 57→import { generateDailyChallenge, loadDailyProgress, loadLeaderboard } from "@/lib/game/beacon"; + 58→ 58→ + 59→ 59→export default function Page() { + 60→ 60→ useGameLoop(); + 61→ 61→ useAudioSync(); + 62→ 62→ useGlobalTide(); + 63→ 63→ const [prestigeOpen, setPrestigeOpen] = useState(false); + 64→ 64→ const [settingsOpen, setSettingsOpen] = useState(false); + 65→ 65→ const [constellationOpen, setConstellationOpen] = useState(false); + 66→ 66→ const [chronicleOpen, setChronicleOpen] = useState(false); + 67→ 67→ const [cruiseOpen, setCruiseOpen] = useState(false); + 68→ 68→ const [mounted, setMounted] = useState(false); + 69→ 69→ + 70→ 70→ const contact = useGameStore((s) => s.contact); + 71→ 71→ const totalDecoded = useGameStore((s) => s.totalDecoded); + 72→ 72→ const crystalsPerSec = useGameStore((s) => s.crystalsPerSec); + 73→ 73→ const ascensions = useGameStore((s) => s.ascensions); + 74→ 74→ const ownedTech = useGameStore((s) => s.tech); + 75→ 75→ const ownedFragments = useGameStore((s) => s.fragments); + 76→ 76→ const ownedAchievements = useGameStore((s) => s.achievements); + 77→ 77→ const ownedConstellation = useGameStore((s) => s.constellation ?? []); + 78→ 78→ const pendingPerkChoices = useGameStore((s) => s.pendingPerkChoices); + 79→ 79→ const crystals = useGameStore((s) => s.crystals); + 80→ 80→ const crystalCap = useGameStore((s) => s.crystalCap); + 81→ 81→ const activeTide = useGameStore((s) => s.activeTide); + 82→ 82→ const globalTide = useGameStore((s) => s.globalTide); + 83→ 83→ const energy = useGameStore((s) => s.energy); + 84→ 84→ const hasActiveExpedition = useGameStore((s) => !!s.activeExpedition && !s.activeExpedition.finished); + 85→ 85→ const chronicleCount = useGameStore((s) => s.chronicle?.length ?? 0); + 86→ 86→ const pendingAttrPoints = useGameStore((s) => s.pendingAttrPoints ?? 0); + 87→ 87→ + 88→ 88→ // 深空信标:检测是否有可领取的奖励(独立 localStorage) + 89→ 89→ const [beaconClaimable, setBeaconClaimable] = useState(false); + 90→ 90→ useEffect(() => { + 91→ 91→ let cancelled = false; + 92→ 92→ const check = () => { + 93→ 93→ try { + 94→ 94→ const c = generateDailyChallenge(); + 95→ 95→ const p = loadDailyProgress(); + 96→ 96→ if (!cancelled) { + 97→ 97→ setBeaconClaimable(p.completedAt !== null && !p.claimed && p.dateKey === c.dateKey); + 98→ 98→ } + 99→ 99→ } catch { + 100→ 100→ if (!cancelled) setBeaconClaimable(false); + 101→ 101→ } + 102→ 102→ }; + 103→ 103→ check(); + 104→ 104→ const id = setInterval(check, 2000); + 105→ 105→ return () => { + 106→ 106→ cancelled = true; + 107→ 107→ clearInterval(id); + 108→ 108→ }; + 109→ 109→ }, []); + 110→ 110→ + 111→ 111→ // 挂载检测:避免持久化 store 在 SSR/CSR 间产生 hydration 不一致 + 112→ 112→ useEffect(() => { + 113→ 113→ // eslint-disable-next-line react-hooks/set-state-in-effect + 114→ 114→ setMounted(true); + 115→ 115→ }, []); + 116→ 116→ + 117→ 117→ // 防止 SSR/CSR 不一致 + 118→ 118→ if (!mounted) { + 119→ 119→ return ( + 120→ 120→
+ 121→ 121→
唤醒回响中…
+ 122→ 122→
+ 123→ 123→ ); + 124→ 124→ } + 125→ 125→ + 126→ 126→ const ownedTechCount = Object.values(ownedTech).filter((v) => v > 0).length; + 127→ 127→ const ownedFragCount = Object.values(ownedFragments).filter(Boolean).length; + 128→ 128→ const ownedAchCount = Object.values(ownedAchievements).filter(Boolean).length; + 129→ 129→ const canPrestige = contact >= 100; + 130→ 130→ const hasPendingPerk = pendingPerkChoices && pendingPerkChoices.length > 0; + 131→ 131→ + 132→ 132→ // 仓库满仓警告 + 133→ 133→ const warehouseFull = crystals >= crystalCap * 0.98; + 134→ 134→ + 135→ 135→ // 目标提示 + 136→ 136→ let goal = "点击中央晶体发起脉冲,累积记忆晶体"; + 137→ 137→ if (totalDecoded === 0 && crystals >= 5) { + 138→ 138→ goal = "右侧「待解码晶体」点击一颗晶体,开始解码"; + 139→ 139→ } else if (totalDecoded > 0 && ownedTechCount === 0) { + 140→ 140→ goal = "用洞见解锁技术树,提升产能"; + 141→ 141→ } else if (totalDecoded > 0 && ownedFragCount < FRAGMENTS.length) { + 142→ 142→ goal = `继续解码,拼凑记忆图谱(${ownedFragCount}/${FRAGMENTS.length})`; + 143→ 143→ } + 144→ 144→ if (energy >= 1 && totalDecoded >= 3) { + 145→ 145→ goal = "「探险」标签可深入遗迹,获取丰厚奖励"; + 146→ 146→ } + 147→ 147→ if (ascensions >= 1 && ownedConstellation.length > 0) { + 148→ 148→ goal = `星图天赋已觉醒 ${ownedConstellation.length}/18,继续飞升获取更多`; + 149→ 149→ } + 150→ 150→ if (warehouseFull) { + 151→ 151→ goal = "⚠ 仓库已满,产能浪费中!请解码晶体或升级仓库"; + 152→ 152→ } + 153→ 153→ if (hasPendingPerk) { + 154→ 154→ goal = "✦ 星图觉醒!点击顶部「星图」按钮选择一道天赋"; + 155→ 155→ } + 156→ 156→ if (activeTide) { + 157→ 157→ const tm = TIDE_EVENTS[activeTide.type]; + 158→ 158→ const prefix = globalTide ? "🌐 全球星潮 · " : ""; + 159→ 159→ goal = `${prefix}${tm.icon} 星潮「${tm.name}」进行中 · ${tm.desc}`; + 160→ 160→ } + 161→ 161→ if (canPrestige) goal = "✦ 接触进度已满,可发起飞升进入新周目"; + 162→ 162→ + 163→ 163→ return ( + 164→ 164→
+ 165→ 165→ {/* 星空背景 */} + 166→ 166→ + 167→ 167→ {/* 星潮背景叠层 */} + 168→ 168→ + 169→ 169→ + 170→ 170→ {/* 顶部 Header */} + 171→ 171→
+ 172→ 172→
+ 173→ 173→
+ 174→ 174→
+ 175→ 175→
+ 176→ 176→
+ 177→ 177→ + 178→ 178→
+ 179→ 179→
+ 180→ 180→
+ 181→ 181→

回响星核

+ 182→ 182→

+ 183→ 183→ ECHO NEXUS · v0.8 + 184→ 184→

+ 185→ 185→
+ 186→ 186→
+ 187→ 187→
+ 188→ 188→ + 189→ 189→ + 200→ 200→ + 215→ 215→ {hasPendingPerk && ( + 216→ 216→ + 225→ 225→ )} + 226→ 226→ {canPrestige && ( + 227→ 227→ + 237→ 237→ )} + 238→ 238→ + 247→ 247→
+ 248→ 248→
+ 249→ 249→ setPrestigeOpen(true)} /> + 250→ 250→
+ 251→ 251→ + 252→ 252→ {/* 主体 — 小屏允许自然滚动,避免 min-h 总和超过视口导致挤压重叠 */} + 253→ 253→
+ 254→ 254→
+ 255→ 255→ {/* 左侧:脉冲晶体 */} + 256→ 256→
+ 257→ 257→ {/* 装饰光圈 */} + 258→ 258→
+ 259→ 259→
+ 260→ 260→ {/* v0.8.2 装饰全息环(填充留白,提升视觉层次) */} + 261→ 261→
+ 262→ 262→
+ 263→ 263→
+ 264→ 264→ {/* 四角全息标记 */} + 265→ 265→
+ 266→ 266→
+ 267→ 267→
+ 268→ 268→
+ 269→ 269→ {/* 顶部状态条 */} + 270→ 270→
+ 271→ 271→ + 272→ 272→ CRYSTAL CORE · ONLINE + 273→ 273→ + 274→ 274→
+ 275→ 275→ {/* 底部铭文 */} + 276→ 276→
+ 277→ 277→ echo · nexus · archaeology + 278→ 278→
+ 279→ 279→
+ 280→ 280→ + 281→ 281→
+ 282→ 282→ {/* v0.9 工单 #2:全息收益信息面板 — 让晶体球价值可视化 */} + 283→ 283→ + 284→ 284→
+ 285→ 285→ + 286→ 286→ {/* 右侧:解码 + 标签面板 */} + 287→ 287→
+ 288→ 288→ {/* 解码面板 */} + 289→ 289→
+ 290→ 290→ + 291→ 291→
+ 292→ 292→ {/* 标签面板:探险 / 技术 / 星图 / 图谱 / 成就 / 信标 / 统计 / 角色 */} + 293→ 293→ {/* v0.9 工单修复:移除 max-h 硬限制,改用 min-h + flex 自适应,避免成就/技术树内容被挤压遮挡 */} + 294→ 294→
+ 295→ 295→ + 296→ 296→ {/* v0.9 工单修复:标签页响应式布局,小屏 4 列 2 行避免拥挤遮挡 */} + 297→ 297→ + 298→ 298→ + 299→ 299→ + 300→ 300→ 探险 + 301→ 301→ {energy >= 1 && !hasActiveExpedition && ( + 302→ 302→ + 303→ 303→ )} + 304→ 304→ {hasActiveExpedition && ( + 305→ 305→ + 306→ 306→ )} + 307→ 307→ + 308→ 308→ + 309→ 309→ + 310→ 310→ 技术 + 311→ 311→ + 312→ 312→ + 313→ 313→ + 314→ 314→ 星图 + 315→ 315→ {hasPendingPerk && ( + 316→ 316→ + 317→ 317→ )} + 318→ 318→ + 319→ 319→ + 320→ 320→ + 321→ 321→ 图谱 + 322→ 322→ + 323→ 323→ + 324→ 324→ + 325→ 325→ 成就 + 326→ 326→ {ownedAchCount < ACHIEVEMENTS.length && ( + 327→ 327→ + 328→ 328→ )} + 329→ 329→ + 330→ 330→ + 331→ 331→ + 332→ 332→ 信标 + 333→ 333→ {beaconClaimable && ( + 334→ 334→ + 335→ 335→ )} + 336→ 336→ + 337→ 337→ + 338→ 338→ + 339→ 339→ 统计 + 340→ 340→ + 341→ 341→ + 342→ 342→ + 343→ 343→ 角色 + 344→ 344→ {pendingAttrPoints > 0 && ( + 345→ 345→ + 346→ 346→ )} + 347→ 347→ + 348→ 348→ + 349→ 349→ + 350→ 350→ + 351→ 351→ + 352→ 352→ + 353→ 353→ + 354→ 354→ + 355→ 355→ + 356→ 356→ + 357→ 357→ + 358→ 358→ + 359→ 359→ + 360→ 360→ + 361→ 361→ + 362→ 362→ + 363→ 363→ + 364→ 364→ + 365→ 365→ + 366→ 366→ + 367→ 367→ + 368→ 368→ + 369→ 369→ + 370→ 370→ + 371→ 371→ + 372→ 372→ + 373→ 373→ + 374→ 374→
+ 375→ 375→
+ 376→ 376→
+ 377→ 377→
+ 378→ 378→ + 379→ 379→ {/* 底部 Footer */} + 380→ 380→
+ 381→ 381→
+ 391→ 391→ + 401→ 401→ ▶ + 402→ 402→ + 403→ 403→ + 408→ 408→ {goal} + 409→ 409→ + 410→ 410→ | + 411→ 411→ + 412→ 412→ 产能 {formatNum(crystalsPerSec)}/s + 413→ 413→ + 414→ 414→ + 421→ 421→ + 422→ 422→ + 423→ 423→
+ 424→ 424→
+ 425→ 425→ + 426→ 426→ + 427→ 427→ + 428→ 428→ + 429→ 429→ + 430→ 430→ + 431→ 431→ + 432→ 432→ + 433→ 433→ + 434→ 434→ {cruiseOpen && setCruiseOpen(false)} />} + 435→ 435→
+ 436→ 436→ ); + 437→ 437→} + 438→ 438→ + 439→ 439→/** + 440→ 440→ * v0.9 工单 #2:全息收益信息面板 + 441→ 441→ * 在中央晶体球下方展示 4 项核心收益指标(产能 / 脉冲 / 连击 / 仓库), + 442→ 442→ * 让玩家明确感知晶体球的价值,缓解"占地方又没用"的反馈。 + 443→ 443→ * 严格四色全息配色:emerald / rose / amber / fuchsia(禁止蓝色/靛色)。 + 444→ 444→ */ + 445→ 445→function CrystalYieldPanel() { + 446→ 446→ const crystalsPerSec = useGameStore((s) => s.crystalsPerSec); + 447→ 447→ const pulsePower = useGameStore((s) => s.pulsePower); + 448→ 448→ const combo = useGameStore((s) => s._combo); + 449→ 449→ const crystals = useGameStore((s) => s.crystals); + 450→ 450→ const crystalCap = useGameStore((s) => s.crystalCap); + 451→ 451→ + 452→ 452→ // 连击倍率:1 + (combo - 1) * 0.15(仅 combo≥1 时生效) + 453→ 453→ const comboMult = combo >= 1 ? 1 + (combo - 1) * 0.15 : 1; + 454→ 454→ const comboHot = combo >= 3; // 连击≥3 高亮闪烁 + 455→ 455→ + 456→ 456→ // 仓库容量百分比 + 457→ 457→ const fillPct = crystalCap > 0 ? (crystals / crystalCap) * 100 : 0; + 458→ 458→ const warehouseWarn = fillPct >= 90; // ≥90% 警告色 + pulse + 459→ 459→ + 460→ 460→ return ( + 461→ 461→
+ 462→ 462→ {/* 晶体产能 — emerald */} + 463→ 463→
+ 464→ 464→ + 465→ 465→
+ 466→ 466→
+ 467→ 467→ 晶体产能 + 468→ 468→
+ 469→ 469→
+ 470→ 470→ {formatNum(crystalsPerSec)} + 471→ 471→ /s + 472→ 472→
+ 473→ 473→
+ 474→ 474→
+ 475→ 475→ + 476→ 476→ {/* 主动脉冲 — rose */} + 477→ 477→
+ 478→ 478→ + 479→ 479→
+ 480→ 480→
+ 481→ 481→ 主动脉冲 + 482→ 482→
+ 483→ 483→
+ 484→ 484→ {formatNum(pulsePower)} + 485→ 485→ {combo >= 1 && ( + 486→ 486→ ×{comboMult.toFixed(2)} + 487→ 487→ )} + 488→ 488→
+ 489→ 489→
+ 490→ 490→
+ 491→ 491→ + 492→ 492→ {/* 当前连击 — amber,≥3 时高亮闪烁 */} + 493→ 493→
+ 498→ 498→ + 503→ 503→
+ 504→ 504→
+ 505→ 505→ 当前连击 + 506→ 506→
+ 507→ 507→
+ 512→ 512→ {combo} + 513→ 513→ /10 + 514→ 514→
+ 515→ 515→
+ 516→ 516→
+ 517→ 517→ + 518→ 518→ {/* 仓库容量 — fuchsia,≥90% 切 rose 警告色 + pulse */} + 519→ 519→
+ 524→ 524→ + 529→ 529→
+ 530→ 530→
+ 535→ 535→ 仓库容量 + 536→ 536→
+ 537→ 537→
+ 542→ 542→ {formatNum(crystals)} + 543→ 543→ + 548→ 548→ /{formatNum(crystalCap)} + 549→ 549→ + 550→ 550→
+ 551→ 551→
+ 552→ 552→
+ 553→ 553→
+ 554→ 554→ ); + 555→ 555→} + 556→ 556→ + 557→ 557→function StatsPanel() { + 558→ 558→ const s = useGameStore(); + 559→ 559→ const achCount = Object.values(s.achievements).filter(Boolean).length; + 560→ 560→ // 深空信标本地排行榜最高分(独立 localStorage) + 561→ 561→ const [beaconBest, setBeaconBest] = useState(null); + 562→ 562→ useEffect(() => { + 563→ 563→ try { + 564→ 564→ const lb = loadLeaderboard(); + 565→ 565→ // eslint-disable-next-line react-hooks/set-state-in-effect + 566→ 566→ setBeaconBest(lb.length > 0 ? lb[0].score : null); + 567→ 567→ } catch { + 568→ 568→ setBeaconBest(null); + 569→ 569→ } + 570→ 570→ }, []); + 571→ 571→ const rows = [ + 572→ 572→ { label: "累计解码晶体", value: `${s.totalDecoded} 颗` }, + 573→ 573→ { label: "飞升周目", value: `${s.ascensions}` }, + 574→ 574→ { label: "持有蓝图", value: `${s.blueprints.length} / 6` }, + 575→ 575→ { label: "已学技术", value: `${Object.values(s.tech).filter((v) => v > 0).length} / ${TECH_TREE.length}` }, + 576→ 576→ { label: "已获碎片", value: `${Object.values(s.fragments).filter(Boolean).length} / ${FRAGMENTS.length}` }, + 577→ 577→ { label: "已解锁成就", value: `${achCount} / ${ACHIEVEMENTS.length}` }, + 578→ 578→ { label: "星图天赋", value: `${s.constellation?.length ?? 0} / ${CONSTELLATION_PERKS.length}` }, + 579→ 579→ { label: "接触进度", value: `${s.contact.toFixed(1)}%` }, + 580→ 580→ { label: "晶体产能", value: `${s.crystalsPerSec.toFixed(2)} /s` }, + 581→ 581→ { label: "仓库上限", value: `${formatNum(s.crystalCap)}` }, + 582→ 582→ { label: "洞见倍率", value: `×${s.insightMult.toFixed(2)}` }, + 583→ 583→ { label: "累计探险", value: `${s.totalExpeditions} 次` }, + 584→ 584→ { label: "BOSS 击破", value: `${s.bossKills ?? 0} 次` }, + 585→ 585→ { label: "星潮亲历", value: `${(s.starTidesEncountered ?? []).length} / 9` }, + 586→ 586→ { label: "编年史条目", value: `${(s.chronicle ?? []).length} 纪元` }, + 587→ 587→ { label: "探险能量", value: `${Math.floor(s.energy)} / ${s.energyMax}` }, + 588→ 588→ { label: "信标最高分", value: beaconBest !== null ? formatNum(beaconBest) : "—" }, + 589→ 589→ { label: "探索力", value: `${s.attributes?.exploration ?? 0} / 100` }, + 590→ 590→ { label: "智慧", value: `${s.attributes?.wisdom ?? 0} / 100` }, + 591→ 591→ { label: "勇气", value: `${s.attributes?.courage ?? 0} / 100` }, + 592→ 592→ { label: "灵感", value: `${s.attributes?.inspiration ?? 0} / 100` }, + 593→ 593→ { label: "待分配属性点", value: `${s.pendingAttrPoints ?? 0}` }, + 594→ 594→ ]; + 595→ 595→ return ( + 596→ 596→
+ 597→ 597→ {rows.map((r) => ( + 598→ 598→
+ 602→ 602→ {r.label} + 603→ 603→ {r.value} + 604→ 604→
+ 605→ 605→ ))} + 606→ 606→
+ 607→ 607→ ); + 608→ 608→} + 609→ 609→ \ No newline at end of file diff --git a/worklog.md b/worklog.md index 425e16553..46eeb55b7 100755 --- a/worklog.md +++ b/worklog.md @@ -856,3 +856,76 @@ Stage Summary: - 在线地址:https://gitea-pages.atdunbg.xyz/Super_Z/echo-nexus/ - 定时任务已重建:job_id=228637,每 15 分钟 webDevReview - 版本:v0.8 → v0.9 + +--- + +## v0.10 工单 #2 大重构 — 空间再分配 + 探索深度(2026-01-24) + +### 触发:Gitea Issue #2 + 用户追加反馈 +- **Gitea Issue #2** "现阶段玩法过于简单,没有可玩性" + - BUG:探险只能一次后再也触发不了 / 元素偏移被遮挡 / 成就UI全遮挡 / 提示框碍眼 + - 问题:占满空屏的没用按钮 / 世界观敷衍 / 成就系统无用 / 技术树太少 +- **用户追加反馈**:80%空间只给一个只能点10次的球 / 长操作UI太小 / 成就框溢出 / 审美疲劳 / 先调研后重构 + +### 调研结论(放置+深空游戏UI设计) +- 《Cookie Clicker》:左右分栏,左侧点击区+右侧建筑列表 +- 《Melvor Idle》:上下分区,顶部导航+主内容区 +- 放置游戏原则:核心循环可见 / 进度可见 / 多面板协同 / 空间感 +- 深空主题应有:星图导航 / 文明遗迹图鉴 / 时序日志 / 雷达扫描 + +### 核心改动(v0.10) + +#### 1. 布局重构 — 空间再分配(page.tsx) +- **旧**:`grid-cols-[1fr_minmax(360px,420px)]` 左 60% 右 40%(晶体球占满) +- **新**:`grid-cols-[minmax(300px,360px)_1fr]` 左 30% 右 70%(晶体球紧凑,右侧扩展) +- 左栏:晶体球(紧凑居中)+ 收益小卡 + **新增探索目标雷达** +- 右栏:解码面板 + 标签页(9个:探险/技术/星图/图谱/成就/信标/统计/角色/图鉴) +- 标签页响应式:小屏 3列3行,大屏 9列1行 +- VLM 验证:左右比例 3:7,晶体球不再占满,无溢出重叠 + +#### 2. 探险 BUG 修复(gameStore.ts + ExpeditionPanel.tsx) +- **根因**:探险结束 `finished=true` 但 `activeExpedition` 不清理,60秒后"收起结算"按钮消失但状态卡死 +- **修复**:`resolveCurrentNode` 和 `abortExpedition` 结束时立即 `activeExpedition: null` +- 保留 `lastExpeditionSummary` 用于入口展示"上次结果"(不限时间) +- 移除 `justFinished` 60秒限制和"收起结算"按钮 +- **验证**:agent-browser 完整跑通 探险→完成→再出发 流程 ✅ + +#### 3. 成就面板重写(AchievementsPanel.tsx) +- **旧问题**:2列网格 + overflow-hidden 导致成就框内元素只显示上半部分 +- **新方案**:单列展示 + min-h-[68px] + line-clamp-2 + flex-1 min-w-0 +- 新增分类过滤标签(全部/已解锁/未解锁,带计数) +- 新增顶部进度条(emerald→rose→fuchsia 渐变) +- 移除 overflow-hidden,改用 line-clamp 控制文本 +- **验证**:VLM 确认"无溢出或截断" ✅ + +#### 4. 新增探索目标雷达(ExplorationRadar.tsx) +- 动态计算 6 类下一目标:技术节点/成就/记忆碎片/探险能量/飞升进度/星图天赋 +- 取前 3 个最相关目标展示,带进度条 + 提示 +- 让晶体球区"等待"变"期待",缓解"占地方没用"反馈 + +#### 5. 新增文明遗迹图鉴系统(relicCodex.ts + RelicCodex.tsx) +- **12 个远古文明遗迹**,每个承载独特故事碎片: + 1. 初次回响(无名先驱)2. 晶体低语(晶语族)3. 虚空方舟(渡厄者) + 4. 寂寂王朝(静默王族)5. 锻星炉(铸星者)6. 忆织者(织忆族) + 7. 蚀相部落(蚀相族)8. 飞升者遗迹 9. 星图密钥(观星者议会) + 10. 编年史之心(史官族)11. 四维之证(原型人格)12. 深空航迹(远航者联盟) +- 每个遗迹:name/race/era/icon/color/brief/story/unlockHint/unlock/reward +- 解锁条件覆盖所有游戏系统(脉冲/解码/探险/BOSS/技术/碎片/星潮/飞升/星图/编年史/属性/巡航) +- UI:2列网格 + 点击展开详细故事 + 累计奖励总览 +- 增加"第九艺术"世界观深度 + +#### 6. 版本号 v0.8 → v0.10 + +### QA 验证(agent-browser + VLM) +- ✅ 布局重构:左右 3:7,晶体球紧凑,探索雷达可见,无溢出 +- ✅ 成就面板:分类过滤可见,无溢出截断,进度条可见 +- ✅ 遗迹图鉴:12个遗迹展示,进度 2/12,故事展开正常 +- ✅ 探险流程:探索节点→前进→完成→自动清理→再出发(totalExp 2→3) +- ✅ lint 零错误 +- ✅ VLM 整体视觉冲击力 8/10 + +### 未解决/下一步 +- 🟡 技术树扩展(用户反馈"太少,分分钟点满")— 待 v0.11 +- 🟡 成就系统价值化(成就解锁主动技能)— 待 v0.11 +- 🟡 VLM 建议:解码区 hover 效果 / 状态文字对比 / 星云背景 +- 🟡 部署 gh-pages