v0.15: 叙事深化 + 放置平衡 + UX 增强

- loreLayers 多层碎片文本(8 碎片 × 2 层 = 16 层深层叙事,5 条暗线交织)
- 永久加成 softcap(≤30 全额 / 30-100 ×0.5 / >100 ×0.25,防滚雪球)
- 批量领取全部工程 + 工程完成 toast 通知
- StatsPanel 独立组件 + idle 统计行(永久加成/完成工程/产出晶体/历史)
- Codex 分层渲染(L0 emerald 表层 / L1 amber 隐秘注脚 / L2 fuchsia 深层回响)
- 修复 v0.14 双重计算 bug(idleTotalPerSec)
- 严格 4 色全息,lint 零错误,agent-browser 端到端验证通过
This commit is contained in:
2026-06-24 12:32:56 +00:00
parent 20dc1f252f
commit 96748d726f
14 changed files with 1168 additions and 2338 deletions
+5 -59
View File
@@ -23,6 +23,7 @@ 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,
@@ -49,11 +50,10 @@ import {
User,
Clock,
} from "lucide-react";
import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config";
import { FRAGMENTS, formatNum } from "@/lib/game/config";
import { ACHIEVEMENTS } from "@/lib/game/achievements";
import { TIDE_EVENTS } from "@/lib/game/starTide";
import { CONSTELLATION_PERKS } from "@/lib/game/constellation";
import { generateDailyChallenge, loadDailyProgress, loadLeaderboard } from "@/lib/game/beacon";
import { generateDailyChallenge, loadDailyProgress } from "@/lib/game/beacon";
import { getUnlockedIdleProjects } from "@/lib/game/idle";
export default function Page() {
@@ -224,7 +224,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.14
ECHO NEXUS · v0.15
</p>
</div>
</div>
@@ -472,58 +472,4 @@ export default function Page() {
);
}
function StatsPanel() {
const s = useGameStore();
const achCount = Object.values(s.achievements).filter(Boolean).length;
// 深空信标本地排行榜最高分(独立 localStorage
const [beaconBest, setBeaconBest] = useState<number | null>(null);
useEffect(() => {
try {
const lb = loadLeaderboard();
// eslint-disable-next-line react-hooks/set-state-in-effect
setBeaconBest(lb.length > 0 ? lb[0].score : null);
} catch {
setBeaconBest(null);
}
}, []);
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.constellation?.length ?? 0} / ${CONSTELLATION_PERKS.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: "BOSS 击破", value: `${s.bossKills ?? 0}` },
{ label: "星潮亲历", value: `${(s.starTidesEncountered ?? []).length} / 6` },
{ label: "编年史条目", value: `${(s.chronicle ?? []).length} 纪元` },
{ label: "探险能量", value: `${Math.floor(s.energy)} / ${s.energyMax}` },
{ label: "信标最高分", value: beaconBest !== null ? formatNum(beaconBest) : "—" },
{ label: "探索力", value: `${s.attributes?.exploration ?? 0} / 100` },
{ label: "智慧", value: `${s.attributes?.wisdom ?? 0} / 100` },
{ label: "勇气", value: `${s.attributes?.courage ?? 0} / 100` },
{ label: "灵感", value: `${s.attributes?.inspiration ?? 0} / 100` },
{ label: "待分配属性点", value: `${s.pendingAttrPoints ?? 0}` },
{ label: "放置永久加成", value: `+${formatNum(s.idlePermanentBonus ?? 0)} /s` },
{ label: "完成放置工程", value: `${s.idleStats?.projectsCompleted ?? 0}` },
{ label: "放置产出晶体", value: formatNum(s.idleStats?.crystalsFromIdle ?? 0) },
];
return (
<div className="grid grid-cols-2 gap-1.5 text-xs">
{rows.map((r) => (
<div
key={r.label}
className="flex items-center justify-between rounded-md bg-black/25 border border-white/5 px-2 py-1.5"
>
<span className="text-muted-foreground">{r.label}</span>
<span className="font-mono text-foreground">{r.value}</span>
</div>
))}
</div>
);
}