Compare commits

..
48 Commits
Author SHA1 Message Date
Super_Z 174d1227c0 68f373b9-921a-4acf-844c-ab7e89ffb727 2026-06-24 22:50:18 +00:00
Z.ai Code 774ddaf9d1 docs: v0.15 部署记录 + main 分叉说明 2026-06-24 22:47:11 +00:00
Super_Z 22588425cf 48c8e466-e804-444f-9f6d-4a2d34b3379a 2026-06-24 12:34:05 +00:00
Super_Z 96748d726f 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 端到端验证通过
2026-06-24 12:32:56 +00:00
Super_Z 20dc1f252f docs: v0.14 README + 放置系统设计文档 + worklog 更新 2026-06-24 10:23:08 +00:00
Super_Z 17820b1f28 v0.14: 放置系统 (Idle Operations) — 采矿无人机舰队 + 6放置工程 + 永久产能加成 + idle徽章 2026-06-24 10:19:40 +00:00
Super_Z 1701a908c9 docs: worklog 更新 v0.8.1 P1 完成记录 2026-06-23 22:35:30 +00:00
Super_Z 71ca5b48a9 v0.8.1: 探险平衡 + 信标系统扩展(周挑战 + 信标链)
P1-a 探险 BOSS 胜率平衡 + 能量恢复(expedition.ts + config.ts + gameStore.ts + ExpeditionPanel.tsx):
- combatWinRate: scale 8→4, floor 0.25→0.35(基础力10对BOSS胜率 25%→35-45%)
- BOSS 难度 5-7→3-5(配合新公式基础胜率达 45-55%)
- computeEnergyRegenInterval(state): 动态恢复间隔
  - exp_2 解锁 -30%, exp_3 解锁 -20%, 探索力属性 -最高30%
  - 下限 12s(原固定 45s)
- computeEnergyRegen 接受 intervalSec 参数
- gameStore tick 传入动态间隔
- config.ts: exp_2/exp_3 描述加'能量恢复 +30%/+20%'
- ExpeditionPanel: 显示实际恢复速度 + '已加速'标记

P1-b 信标系统扩展(beacon.ts + BeaconPanel.tsx + gameStore.ts)[subagent 9-b]:
- 周挑战: getWeekKey ISO 8601 + FNV-1a 种子确定性生成
  - 难度强制 anomaly 60%/singular 40%, goal 日挑战×3-5倍
  - 完整进度/领奖/排行榜推送(isWeekly标记)
- 信标链: 4里程碑(3/7/14/30天) + grace续命机制(每链1次)
  - recordChainCompletion 核心断链/续命逻辑
  - 奖励 50→680洞见递增
- BeaconPanel: 周挑战fuchsia主题 + 信标链amber→rose渐变里程碑节点
- gameStore: trackBeacon升级 + claimWeeklyBeacon/claimChainReward actions
- VLM 8/10, lint零错误, 5场景bun测试全PASS
2026-06-23 22:34:52 +00:00
Super_Z a3aa381656 docs: worklog 更新 v0.8 完成记录 + 工单修正 + cron 228357 2026-06-23 21:31:26 +00:00
Super_Z 494bc5ff64 v0.8: 巡航玩法大增强 - BOSS 战 + 道具掉落 + 事件选择节点
- cruise.ts (+1487行): BossEntity/BossPhase/BulletEntity/PowerupEntity/UpgradeCard 类型
  - BOSS 三阶段攻击(直射/散射/追踪弹+召唤), HP=100+lvl×20
  - 飞船射击系统(空格/射击按钮, 能量消耗5/发, 150ms冷却)
  - 5种道具掉落(shield/energy/rapid/overdrive/magnet)
  - 10种升级牌(速度/护盾/能量/火力/吸引/无敌等), 每关3选1
  - shootShip/applyUpgradeCard/rollUpgradeChoices 函数
  - generateLevel: level%5===0 生成BOSS关
- CruiseMode.tsx (+858行): BOSS多层环+HP条+触手绘制, 子弹尾焰, 道具六边形+图标
  - buff状态指示器, 选择升级阶段3张玻璃卡牌翻转
  - 射击按钮(移动端右下), BOSS HP条(顶部), buff列表(左侧)
  - 修复 lint: isTouch/stats 改用 useState initializer
- page.tsx: 版本号 v0.7 → v0.8
- worklog.md: 工单修正(标注v0.7已完成), cron job_id 228357重建
2026-06-23 21:30:43 +00:00
Super_Z 43dfc2d41b docs: README 添加在线游玩链接 + v0.7 状态更新 2026-06-23 21:10:47 +00:00
Super_Z 3d916a1574 0804500b-ffe8-4b5f-8419-5e7aafd9fc73 2026-06-23 21:01:32 +00:00
Super_Z 4545fe380b v0.7: CrystalOrb Canvas 粒子系统升级 + 角色属性系统(四维)
CrystalOrb 升级(P0 视觉震撼):
- 从 CSS 动画重写为 Canvas 2D 粒子系统
- 环绕能量粒子(3层32个,emerald/fuchsia/rose,带拖尾)
- 环境星尘(40个,闪烁漂移)
- 点击爆发粒子 + 冲击波环(连击解锁更多层)
- 晶核辉光呼吸 + 3层六边形纹理 + 鼠标视差
- VLM 视觉评分 8/10

角色属性系统(P1 RPG 深度):
- 新增 attributes.ts 逻辑层 + AttributesPanel UI
- 四维属性:探索力/智慧/勇气/灵感(emerald/fuchsia/amber/rose)
- 两区加成公式(0-50线性,50-100递减)
- 飞升获得属性点(ascensions×2+1),手动分配
- 完成活动获得属性经验,自动升级
- 接入 pulse/clickNode/resolveCurrentNode/grantCruiseReward/tickTide
- 旧存档兼容(migrateAttributes)
- 新增 2 项成就 + 第 8 个标签页「角色」
- VLM 视觉评分 7-8/10

lint 零错误;HTTP 200
2026-06-23 21:00:42 +00:00
Super_Z 8474f4caa7 0c799010-d26c-48d2-8c38-f465011f59d8 2026-06-23 20:32:33 +00:00
Super_Z c0535e6ebe d3c86ebe-91f4-4f29-a530-6cb46cab2abc 2026-06-23 20:19:46 +00:00
Super_Z 7111e154b2 906d0c00-e2bc-4edd-ae26-665c03a1ba37 2026-06-23 20:10:42 +00:00
Super_Z 72e1f3fc3c da4fe2ad-c3b9-41fc-82e7-336a69a7947c 2026-06-23 19:51:59 +00:00
Super_Z fafa2ee20a v0.6: 深空巡航 Canvas 2D 实时玩法 + UI 偏移重叠修复
## 玩法拓展(回应用户: 玩法太单调/全是点点点)
- 新增「深空巡航」Canvas 2D 全屏实时玩法
  - 飞船操控(WASD/方向键/移动端虚拟摇杆)
  - 实时躲避陨石/虚空风暴,收集晶体/洞见/信标
  - 到达星门通关,护盾归零失败
  - 3层视差星空 + 粒子系统(尾焰/收集/碰撞/烟花) + 辉光 + 屏幕震动
  - 奖励同步主游戏(grantCruiseReward)
  - 独立 localStorage 记录最高分/通关数

## UI 修复(回应用户: 解析球左上偏移/成就小屏重叠)
- DecodeArray 节点: 移除 Tailwind translate 与 inline transform 冲突
- AchievementsPanel: 移除固定 max-h, 改 flex-1 min-h-0 自适应
- page.tsx 布局: 小屏减小 min-h, 避免挤压重叠

## 集成
- gameStore 新增 grantCruiseReward action
- header 新增 amber 主题「巡航」按钮 + 全屏 CruiseMode 渲染

## 文件
- src/lib/game/cruise.ts (新增, ~1008行)
- src/components/game/CruiseMode.tsx (新增, ~1350行)
- src/app/page.tsx (集成巡航入口)
- src/store/gameStore.ts (grantCruiseReward action)
- src/components/game/DecodeArray.tsx (节点偏移修复)
- src/components/game/AchievementsPanel.tsx (小屏重叠修复)

QA: lint 零错误; agent-browser+VLM 验证主界面无偏移+巡航玩法正常运行
2026-06-23 19:33:51 +00:00
Super_Z fcf94b6c3f docs: update worklog for v0.5.2 (offline report + persist fix) 2026-06-23 17:04:58 +00:00
Super_Z 296c609479 v0.5.2: offline report + tab UI + persist race fix
- New: Offline earnings report dialog (welcome back popup with gain/duration/rate/progress)
  - src/lib/game/offlineReport.ts: module-level store + custom event
  - src/components/game/OfflineReportDialog.tsx: Radix Dialog UI
  - gameStore.init: setPendingOfflineReport before set()
- Fix: useGameLoop waits for persist.hasHydrated() before init
  - Root cause: Zustand persist rehydrate is async (Promise.resolve wrapper)
  - Before fix: init read INITIAL_STATE (lastTick=undefined), skipped offline branch
  - After fix: init waits for rehydrate, correctly reads saved lastTick
- UI: Tab bar redesign with per-tab accent colors + flex-col layout
- Version: v0.5 -> v0.5.2
2026-06-23 17:04:28 +00:00
Super_Z bd28219b65 docs: update worklog for v0.5.1 (Gitea Pages deploy + tutorial + UI) 2026-06-23 16:38:44 +00:00
Super_Z 8605f9bb5f chore: remove QA screenshots from repo + gitignore
- Remove qa-static-*.png and qa-tut-01.png (temporary QA artifacts)
- Add qa-*.png to .gitignore
2026-06-23 16:37:49 +00:00
Super_Z 00a46a0bf9 Deploy: v0.5.1 static export
- New: Tutorial system (7-step onboarding with spotlight + action detection)
- UI: ResourceBar spacing optimization (gap, dividers, tabular-nums)
- UI: SettingsDialog adds tutorial replay + online play link
- Anim: tutorial pulse + pop-in keyframes
2026-06-23 16:34:00 +00:00
Super_Z 084387174b 69e481d9-d5e1-44d1-9561-71b5173e04bb 2026-06-23 15:25:33 +00:00
Super_Z ab511fe9e0 chore: update docs submodule ref (v0.5 beacon docs) 2026-06-23 15:24:51 +00:00
Super_Z 60445abd42 v0.5: 深空信标系统 + 编年史历史 BUG 修复
新功能:深空信标(Deep Space Beacon)
- 每日挑战:UTC 日期种子确定性生成,5 种类型 × 3 档难度
- 本地排行榜 Top 20,奖牌图标 + 难度色点 + 今日高亮
- 进度追踪独立 localStorage,不污染 GameState
- 倒计时 + 领取奖励发放到游戏状态

BUG 修复:编年史历史条目显示原始 tide 键名
- 新增 regenerateLoreFromEntry(),显示时重新生成 lore
- 修复 v0.4 之前条目 lore 中 tide_ruins 等原始键名

UI:第 7 标签页「信标」+ 统计面板「信标最高分」行
版本号 v0.4 → v0.5

详见 docs/10-深空信标系统-v0.5.md
2026-06-23 15:24:51 +00:00
Super_Z 04f6a398f4 735ccca3-a163-4b49-8cb2-c04c3a043ee7 2026-06-23 15:24:51 +00:00
Super_Z a02b6191c5 fix: websocket example eslint set-state-in-effect 2026-06-23 15:24:24 +00:00
Super_Z 5bc041ac0b docs: v0.5 深空信标系统文档 2026-06-23 15:24:24 +00:00
Super_Z 697e1f360a docs: 更新 worklog 至 v0.4 完成 2026-06-23 14:48:15 +00:00
Super_Z e6ec22132e docs: v0.4 回响编年史系统设计文档 2026-06-23 14:46:06 +00:00
Super_Z e69c48ba69 feat(v0.4): 回响编年史 — 跨周目叙事时间轴系统
- 新增 ChronicleEntry / RunStartSnapshot 类型与 GameState 字段
- 新建 chronicle.ts: 5 纪元名生成、模板化叙事、里程碑检测、run delta 计算
- engine.ts performPrestige 构建编年史条目并重置 runStart
- gameStore: init 兼容旧存档 + tickTide 记录星潮 + resolveCurrentNode 计 BOSS 击杀
- chooseConstellationPerk 回填最近一条 entry 的 perksThisAscension
- 新建 ChronicleDialog.tsx: 时间轴 UI + 5 纪元循环色 + 入场动画
- 新增 6 项成就: 首部编年/三纪元回响/五纪元闭环/首杀维度/维度猎手/星潮亲历者
- 新增 chronicle / chronicleOpen 程序化音效
- 修复关键 BUG: PrestigeDialog disabled 逻辑错误导致玩家永远无法飞升
- 修复 UX: newBp=0 时不再永久禁用飞升按钮(v0.3.1 星图+v0.4 编年史已提供动机)
- 统计面板新增 BOSS 击破/星潮亲历/编年史条目 3 行
- 版本号升至 v0.4
2026-06-23 14:45:38 +00:00
Super_Z 98fbbf1634 docs: 更新 worklog v0.3.1 收尾记录 2026-06-23 14:15:57 +00:00
Super_Z 9a17849859 chore: 清理未用变量 + 新增星图天赋目标提示 + ignore skills/ 2026-06-23 14:15:29 +00:00
Super_Z cc02fc2e7f Merge branch 'main' of https://git.atdunbg.xyz/Super_Z/echo-nexus
# Conflicts:
#	.gitignore
2026-06-23 14:13:30 +00:00
Super_Z 9f4d839c6b feat(v0.3.1): 星图天文台元进程天赋系统 — 18 天赋 / 3 选 1 draft / Canvas 动态星图
- 新增 src/lib/game/constellation.ts:6 类别 × 3 天赋 = 18 个永久天赋,
  constellationBonuses() 聚合 19 项修饰器,rollPerkChoices() 保证不同类别
- 状态扩展:GameState.constellation / pendingPerkChoices
- engine.ts: recomputeStats 聚合三层加成(技术+蓝图+成就+星图),
  performPrestige 触发天赋选择 + 飞升礼包补偿,新增 rollCrystalTierWithBonus
- expedition.ts: 探险力/生命应用星座修饰器
- gameStore.ts: 新增 chooseConstellationPerk + rerollPerkChoices 动作,
  全链路接入(tickTide/autoDecodeTick/clickNode 等用点)
- 新增 ConstellationPanel.tsx: Canvas 动态星图(六边形 6 星座 × 3 星点,
  闪烁/光晕/十字光线/连接线/中心星核呼吸)+ Hover 提示 + 类别图例
- 新增 ConstellationDialog.tsx: 飞升后自动弹出,3 卡片 draft + 重新抽取
- PrestigeDialog: 增加「星图觉醒预告」卡片
- page.tsx: 6 列标签栏 + 顶部「觉醒」按钮 + 统计面板加星图天赋
- audio.ts: 新增 constellation SFX(上升琶音 + 高频闪光)
- achievements.ts: 新增「星图初绘」「六分星辉」2 项成就
- QA: agent-browser + VLM 全流程通过;lint 零错误;编译 < 200ms
- 二次回应 Issue #1:从「连连看」扩展为 6 大玩法层 + 元进程 draft
2026-06-23 14:12:37 +00:00
Super_Z 433c19cc73 docs: v0.3.1 星图天文台元进程天赋系统设计文档 2026-06-23 14:12:19 +00:00
Super_Z f3d2e53e4f 42773b7e-7147-4ad5-828d-da7fcf0290b8 2026-06-23 13:52:13 +00:00
Super_Z 27bb1555e5 feat(v0.3): 星潮事件系统 - 6种周期性全局玩法修饰
- 6种星潮: 晶体潮(产能x2)/谐振风暴(解码x1.6)/遗迹共振(探险+5力+30血)/虚空低语(洞见x2)/星核悸动(接触x3脉冲x2)/寂静期(产能x0.5,结束补偿洞见)
- 触发: 首次30s, 常规间隙40s, 持续75s, 权重抽取, 飞升重置
- UI: 顶部指示器芯片(倒计时) + 全屏背景叠层(色带+呼吸光) + Toast通知 + Footer联动
- 音效: tideStart(神秘扫频) + tideEnd(柔和消退), 程序化合成
- 修饰器: 产能/脉冲/解码奖励即时乘, 洞见/接触率进缓存, 探险力/血量即时加
- QA: agent-browser全流程通过(触发/指示器/叠层/寂静期补偿), VLM视觉确认, lint零错误
2026-06-23 13:51:32 +00:00
Super_Z da18f32df2 3d8566bc-063f-41e8-8934-68c9ac5b8211 2026-06-23 13:38:23 +00:00
Super_Z 51714d2532 feat(v0.2.1): 程序化音频系统 + 14项成就系统 + 脉冲粒子打磨
- 音频: Web Audio API 程序化合成 15 种音效(脉冲/解码/探险/飞升/成就), 零资源文件
- 成就: 14项成就, 跨周目永久产能/洞见加成, Toast通知+专属音效
- 视觉: 脉冲粒子爆发, 扩散环, 浮动数字按连击变色
- UI: 标签栏5列(+成就页), 统计面板扩展, 设置页音效试听
- 兼容: 旧存档自动补全 achievements 字段
2026-06-23 13:36:44 +00:00
Super_Z 674775be0e 6f5491c0-6c94-4f8e-8ad3-136d04ab862f 2026-06-23 13:04:15 +00:00
Super_Z 84749c218a feat(v0.2): 遗迹探险肉鸽系统 + 仓库满仓警告
新增 v0.2 旗舰功能 - 遗迹探险肉鸽系统:
- 能量系统: 3初始/5上限, 每45秒恢复1点
- 探险力/生命: 由技术树+飞升蓝图计算
- 6种节点类型: 战斗/宝藏/抉择/解谜/休整/BOSS
- 程序化路径: 5-7节点线性, 末位BOSS
- 实时奖励入账, 失败保留奖励, 可主动撤退
- 探险日志(最近30条)

配套修复:
- HP clamp(休整不超上限)
- 探险奖励绕过仓库上限(仅闲置产能受限)
- 仓库满仓UX警告(底部状态栏琥珀色脉冲)
- 技术树探险分支增强(探险力/生命加成)

UI: 探险标签页(入口态/进行态), 节点路径图, 事件卡, 生命/能量条
验证: 全流程通过, 奖励绕过cap验证(101>50)
2026-06-23 13:02:45 +00:00
Super_Z f73b96026c d9404a31-1970-4e79-ac9a-f4f9db60350b 2026-06-23 12:47:49 +00:00
Super_Z 5eb303c389 fix(decode): 修复解码谜题大量无解问题 (v0.1.1)
核心问题: generatePuzzle 随机填色未保证有解, 导致大量死局

修复:
- 路径构造法生成器: 主动构造一条保证可解的路径, 100%可解率
- isSolvable DFS精确判定: 检测此路不通, 提示撤销
- undoStep 撤销功能: 走错单步回退, 无需整局重排
- 卡死提示横幅: 局面无解时醒目提示+快捷撤销
- canStartFrom 可行起点高亮: 选起点阶段标记可行/死起点
- 旧存档兼容: init时清理不可解的遗留谜题

验证: 3000/3000可解, agent-browser T1/T2端到端通过
2026-06-23 12:46:35 +00:00
Super_Z 9e520a34c6 docs: 初始化游戏调研、设计文档与技术架构 (v0.1 设计定稿)
- 01-调研报告: 浏览器/HTML5市场、独立游戏品类、放置品类、留存洞察、技术选型
- 02-游戏设计文档: 世界观、核心循环、六大系统、MVP范围、路线图
- 03-技术架构: 栈选型、目录结构、状态模型、解码引擎、Canvas策略
- README: 项目概览
2026-06-23 12:05:10 +00:00
Super_Z 1e706b2ec0 Initial commit 2026-06-23 20:02:20 +08:00
Z User e193530f24 Initial commit 2026-06-23 11:50:15 +00:00
248 changed files with 34810 additions and 174 deletions
Executable
+1
View File
@@ -0,0 +1 @@
DATABASE_URL=file:/home/z/my-project/db/custom.db
Executable
+150
View File
@@ -0,0 +1,150 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Echo Nexus 本地文件
local-*
.claude
.z-ai-config
dev.log
dev.out.log
*.tsbuildinfo
next-env.d.ts
.vercel
skills/
qa-*.png
+122
View File
@@ -0,0 +1,122 @@
#!/bin/bash
# 将 stderr 重定向到 stdout,避免 execute_command 因为 stderr 输出而报错
exec 2>&1
set -e
# 获取脚本所在目录(.zscripts 目录,即 workspace-agent/.zscripts
# 使用 $0 获取脚本路径(兼容 sh 和 bash)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Next.js 项目路径
NEXTJS_PROJECT_DIR="/home/z/my-project"
# 检查 Next.js 项目目录是否存在
if [ ! -d "$NEXTJS_PROJECT_DIR" ]; then
echo "❌ 错误: Next.js 项目目录不存在: $NEXTJS_PROJECT_DIR"
exit 1
fi
echo "🚀 开始构建 Next.js 应用和 mini-services..."
echo "📁 Next.js 项目路径: $NEXTJS_PROJECT_DIR"
# 切换到 Next.js 项目目录
cd "$NEXTJS_PROJECT_DIR" || exit 1
# 设置环境变量
export NEXT_TELEMETRY_DISABLED=1
BUILD_DIR="/tmp/build_fullstack_$BUILD_ID"
echo "📁 清理并创建构建目录: $BUILD_DIR"
mkdir -p "$BUILD_DIR"
# 安装依赖
echo "📦 安装依赖..."
bun install
# 构建 Next.js 应用
echo "🔨 构建 Next.js 应用..."
bun run build
# 构建 mini-services
# 检查 Next.js 项目目录下是否有 mini-services 目录
if [ -d "$NEXTJS_PROJECT_DIR/mini-services" ]; then
echo "🔨 构建 mini-services..."
# 使用 workspace-agent 目录下的 mini-services 脚本
sh "$SCRIPT_DIR/mini-services-install.sh"
sh "$SCRIPT_DIR/mini-services-build.sh"
# 复制 mini-services-start.sh 到 mini-services-dist 目录
echo " - 复制 mini-services-start.sh 到 $BUILD_DIR"
cp "$SCRIPT_DIR/mini-services-start.sh" "$BUILD_DIR/mini-services-start.sh"
chmod +x "$BUILD_DIR/mini-services-start.sh"
else
echo "️ mini-services 目录不存在,跳过"
fi
# 将所有构建产物复制到临时构建目录
echo "📦 收集构建产物到 $BUILD_DIR..."
# 复制 Next.js standalone 构建输出
if [ -d ".next/standalone" ]; then
echo " - 复制 .next/standalone"
cp -r .next/standalone "$BUILD_DIR/next-service-dist/"
fi
# 复制 Next.js 静态文件
if [ -d ".next/static" ]; then
echo " - 复制 .next/static"
mkdir -p "$BUILD_DIR/next-service-dist/.next"
cp -r .next/static "$BUILD_DIR/next-service-dist/.next/"
fi
# 复制 public 目录
if [ -d "public" ]; then
echo " - 复制 public"
cp -r public "$BUILD_DIR/next-service-dist/"
fi
# 将测试环境数据库复制到构建产物中,生产环境直接使用这份数据库
if [ -f "./db/custom.db" ]; then
echo "🗄️ 复制测试环境数据库到构建产物..."
mkdir -p "$BUILD_DIR/db"
cp -r ./db/. "$BUILD_DIR/db/"
echo "🗄️ 同步构建产物中的数据库结构..."
DATABASE_URL="file:$BUILD_DIR/db/custom.db" bun run db:push
echo "✅ 构建产物数据库已准备完成"
ls -lah "$BUILD_DIR/db"
else
echo "❌ 未找到测试环境数据库文件 ./db/custom.db,无法继续构建生产包"
exit 1
fi
# 复制 Caddyfile(如果存在)
if [ -f "Caddyfile" ]; then
echo " - 复制 Caddyfile"
cp Caddyfile "$BUILD_DIR/"
else
echo "️ Caddyfile 不存在,跳过"
fi
# 复制 start.sh 脚本
echo " - 复制 start.sh 到 $BUILD_DIR"
cp "$SCRIPT_DIR/start.sh" "$BUILD_DIR/start.sh"
chmod +x "$BUILD_DIR/start.sh"
# 打包到 $BUILD_DIR.tar.gz
PACKAGE_FILE="${BUILD_DIR}.tar.gz"
echo ""
echo "📦 打包构建产物到 $PACKAGE_FILE..."
cd "$BUILD_DIR" || exit 1
tar -czf "$PACKAGE_FILE" .
cd - > /dev/null || exit 1
# # 清理临时目录
# rm -rf "$BUILD_DIR"
echo ""
echo "✅ 构建完成!所有产物已打包到 $PACKAGE_FILE"
echo "📊 打包文件大小:"
ls -lh "$PACKAGE_FILE"
+1
View File
@@ -0,0 +1 @@
1061
+154
View File
@@ -0,0 +1,154 @@
#!/bin/bash
set -euo pipefail
# 获取脚本所在目录(.zscripts)
# 使用 $0 获取脚本路径(与 build.sh 保持一致)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
log_step_start() {
local step_name="$1"
echo "=========================================="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting: $step_name"
echo "=========================================="
export STEP_START_TIME
STEP_START_TIME=$(date +%s)
}
log_step_end() {
local step_name="${1:-Unknown step}"
local end_time
end_time=$(date +%s)
local duration=$((end_time - STEP_START_TIME))
echo "=========================================="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Completed: $step_name"
echo "[LOG] Step: $step_name | Duration: ${duration}s"
echo "=========================================="
echo ""
}
start_mini_services() {
local mini_services_dir="$PROJECT_DIR/mini-services"
local started_count=0
log_step_start "Starting mini-services"
if [ ! -d "$mini_services_dir" ]; then
echo "Mini-services directory not found, skipping..."
log_step_end "Starting mini-services"
return 0
fi
echo "Found mini-services directory, scanning for sub-services..."
for service_dir in "$mini_services_dir"/*; do
if [ ! -d "$service_dir" ]; then
continue
fi
local service_name
service_name=$(basename "$service_dir")
echo "Checking service: $service_name"
if [ ! -f "$service_dir/package.json" ]; then
echo "[$service_name] No package.json found, skipping..."
continue
fi
if ! grep -q '"dev"' "$service_dir/package.json"; then
echo "[$service_name] No dev script found, skipping..."
continue
fi
echo "Starting $service_name in background..."
(
cd "$service_dir"
echo "[$service_name] Installing dependencies..."
bun install
echo "[$service_name] Running bun run dev..."
exec bun run dev
) >"$PROJECT_DIR/.zscripts/mini-service-${service_name}.log" 2>&1 &
local service_pid=$!
echo "[$service_name] Started in background (PID: $service_pid)"
echo "[$service_name] Log: $PROJECT_DIR/.zscripts/mini-service-${service_name}.log"
disown "$service_pid" 2>/dev/null || true
started_count=$((started_count + 1))
done
echo "Mini-services startup completed. Started $started_count service(s)."
log_step_end "Starting mini-services"
}
wait_for_service() {
local host="$1"
local port="$2"
local service_name="$3"
local max_attempts="${4:-60}"
local attempt=1
echo "Waiting for $service_name to be ready on $host:$port..."
while [ "$attempt" -le "$max_attempts" ]; do
if curl -s --connect-timeout 2 --max-time 5 "http://$host:$port" >/dev/null 2>&1; then
echo "$service_name is ready!"
return 0
fi
echo "Attempt $attempt/$max_attempts: $service_name not ready yet, waiting..."
sleep 1
attempt=$((attempt + 1))
done
echo "ERROR: $service_name failed to start within $max_attempts seconds"
return 1
}
cleanup() {
if [ -n "${DEV_PID:-}" ] && kill -0 "$DEV_PID" >/dev/null 2>&1; then
echo "Stopping Next.js dev server (PID: $DEV_PID)..."
kill "$DEV_PID" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT INT TERM
cd "$PROJECT_DIR"
if ! command -v bun >/dev/null 2>&1; then
echo "ERROR: bun is not installed or not in PATH"
exit 1
fi
log_step_start "bun install"
echo "[BUN] Installing dependencies..."
bun install
log_step_end "bun install"
log_step_start "bun run db:push"
echo "[BUN] Setting up database..."
bun run db:push
log_step_end "bun run db:push"
log_step_start "Starting Next.js dev server"
echo "[BUN] Starting development server..."
bun run dev &
DEV_PID=$!
log_step_end "Starting Next.js dev server"
log_step_start "Waiting for Next.js dev server"
wait_for_service "localhost" "3000" "Next.js dev server"
log_step_end "Waiting for Next.js dev server"
log_step_start "Health check"
echo "[BUN] Performing health check..."
curl -fsS localhost:3000 >/dev/null
echo "[BUN] Health check passed"
log_step_end "Health check"
start_mini_services
echo "Next.js dev server is running in background (PID: $DEV_PID)."
echo "Use 'kill $DEV_PID' to stop it."
disown "$DEV_PID" 2>/dev/null || true
unset DEV_PID
+78
View File
@@ -0,0 +1,78 @@
#!/bin/bash
# 配置项
ROOT_DIR="/home/z/my-project/mini-services"
DIST_DIR="/tmp/build_fullstack_$BUILD_ID/mini-services-dist"
main() {
echo "🚀 开始批量构建..."
# 检查 rootdir 是否存在
if [ ! -d "$ROOT_DIR" ]; then
echo "️ 目录 $ROOT_DIR 不存在,跳过构建"
return
fi
# 创建输出目录(如果不存在)
mkdir -p "$DIST_DIR"
# 统计变量
success_count=0
fail_count=0
# 遍历 mini-services 目录下的所有文件夹
for dir in "$ROOT_DIR"/*; do
# 检查是否是目录且包含 package.json
if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then
project_name=$(basename "$dir")
# 智能查找入口文件 (按优先级查找)
entry_path=""
for entry in "src/index.ts" "index.ts" "src/index.js" "index.js"; do
if [ -f "$dir/$entry" ]; then
entry_path="$dir/$entry"
break
fi
done
if [ -z "$entry_path" ]; then
echo "⚠️ 跳过 $project_name: 未找到入口文件 (index.ts/js)"
continue
fi
echo ""
echo "📦 正在构建: $project_name..."
# 使用 bun build CLI 构建
output_file="$DIST_DIR/mini-service-$project_name.js"
if bun build "$entry_path" \
--outfile "$output_file" \
--target bun \
--minify; then
echo "$project_name 构建成功 -> $output_file"
success_count=$((success_count + 1))
else
echo "$project_name 构建失败"
fail_count=$((fail_count + 1))
fi
fi
done
if [ -f ./.zscripts/mini-services-start.sh ]; then
cp ./.zscripts/mini-services-start.sh "$DIST_DIR/mini-services-start.sh"
chmod +x "$DIST_DIR/mini-services-start.sh"
fi
echo ""
echo "🎉 所有任务完成!"
if [ $success_count -gt 0 ] || [ $fail_count -gt 0 ]; then
echo "✅ 成功: $success_count"
if [ $fail_count -gt 0 ]; then
echo "❌ 失败: $fail_count"
fi
fi
}
main
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
# 配置项
ROOT_DIR="/home/z/my-project/mini-services"
main() {
echo "🚀 开始批量安装依赖..."
# 检查 rootdir 是否存在
if [ ! -d "$ROOT_DIR" ]; then
echo "️ 目录 $ROOT_DIR 不存在,跳过安装"
return
fi
# 统计变量
success_count=0
fail_count=0
failed_projects=""
# 遍历 mini-services 目录下的所有文件夹
for dir in "$ROOT_DIR"/*; do
# 检查是否是目录且包含 package.json
if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then
project_name=$(basename "$dir")
echo ""
echo "📦 正在安装依赖: $project_name..."
# 进入项目目录并执行 bun install
if (cd "$dir" && bun install); then
echo "$project_name 依赖安装成功"
success_count=$((success_count + 1))
else
echo "$project_name 依赖安装失败"
fail_count=$((fail_count + 1))
if [ -z "$failed_projects" ]; then
failed_projects="$project_name"
else
failed_projects="$failed_projects $project_name"
fi
fi
fi
done
# 汇总结果
echo ""
echo "=================================================="
if [ $success_count -gt 0 ] || [ $fail_count -gt 0 ]; then
echo "🎉 安装完成!"
echo "✅ 成功: $success_count"
if [ $fail_count -gt 0 ]; then
echo "❌ 失败: $fail_count"
echo ""
echo "失败的项目:"
for project in $failed_projects; do
echo " - $project"
done
fi
else
echo "️ 未找到任何包含 package.json 的项目"
fi
echo "=================================================="
}
main
+123
View File
@@ -0,0 +1,123 @@
#!/bin/sh
# 配置项
DIST_DIR="./mini-services-dist"
# 存储所有子进程的 PID
pids=""
# 清理函数:优雅关闭所有服务
cleanup() {
echo ""
echo "🛑 正在关闭所有服务..."
# 发送 SIGTERM 信号给所有子进程
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
echo " 关闭进程 $pid ($service_name)..."
kill -TERM "$pid" 2>/dev/null
fi
done
# 等待所有进程退出(最多等待 5 秒)
sleep 1
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
# 如果还在运行,等待最多 4 秒
timeout=4
while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
sleep 1
timeout=$((timeout - 1))
done
# 如果仍然在运行,强制关闭
if kill -0 "$pid" 2>/dev/null; then
echo " 强制关闭进程 $pid..."
kill -KILL "$pid" 2>/dev/null
fi
fi
done
echo "✅ 所有服务已关闭"
}
main() {
echo "🚀 开始启动所有 mini services..."
# 检查 dist 目录是否存在
if [ ! -d "$DIST_DIR" ]; then
echo "️ 目录 $DIST_DIR 不存在"
return
fi
# 查找所有 mini-service-*.js 文件
service_files=""
for file in "$DIST_DIR"/mini-service-*.js; do
if [ -f "$file" ]; then
if [ -z "$service_files" ]; then
service_files="$file"
else
service_files="$service_files $file"
fi
fi
done
# 计算服务文件数量
service_count=0
for file in $service_files; do
service_count=$((service_count + 1))
done
if [ $service_count -eq 0 ]; then
echo "️ 未找到任何 mini service 文件"
return
fi
echo "📦 找到 $service_count 个服务,开始启动..."
echo ""
# 启动每个服务
for file in $service_files; do
service_name=$(basename "$file" .js | sed 's/mini-service-//')
echo "▶️ 启动服务: $service_name..."
# 使用 bun 运行服务(后台运行)
bun "$file" &
pid=$!
if [ -z "$pids" ]; then
pids="$pid"
else
pids="$pids $pid"
fi
# 等待一小段时间检查进程是否成功启动
sleep 0.5
if ! kill -0 "$pid" 2>/dev/null; then
echo "$service_name 启动失败"
# 从字符串中移除失败的 PID
pids=$(echo "$pids" | sed "s/\b$pid\b//" | sed 's/ */ /g' | sed 's/^ *//' | sed 's/ *$//')
else
echo "$service_name 已启动 (PID: $pid)"
fi
done
# 计算运行中的服务数量
running_count=0
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
running_count=$((running_count + 1))
fi
done
echo ""
echo "🎉 所有服务已启动!共 $running_count 个服务正在运行"
echo ""
echo "💡 按 Ctrl+C 停止所有服务"
echo ""
# 等待所有后台进程
wait
}
main
+135
View File
@@ -0,0 +1,135 @@
#!/bin/sh
set -e
# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BUILD_DIR="$SCRIPT_DIR"
# 存储所有子进程的 PID
pids=""
# 清理函数:优雅关闭所有服务
cleanup() {
echo ""
echo "🛑 正在关闭所有服务..."
# 发送 SIGTERM 信号给所有子进程
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
echo " 关闭进程 $pid ($service_name)..."
kill -TERM "$pid" 2>/dev/null
fi
done
# 等待所有进程退出(最多等待 5 秒)
sleep 1
for pid in $pids; do
if kill -0 "$pid" 2>/dev/null; then
# 如果还在运行,等待最多 4 秒
timeout=4
while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
sleep 1
timeout=$((timeout - 1))
done
# 如果仍然在运行,强制关闭
if kill -0 "$pid" 2>/dev/null; then
echo " 强制关闭进程 $pid..."
kill -KILL "$pid" 2>/dev/null
fi
fi
done
echo "✅ 所有服务已关闭"
exit 0
}
echo "🚀 开始启动所有服务..."
echo ""
# 切换到构建目录
cd "$BUILD_DIR" || exit 1
ls -lah
DEFAULT_PACKAGED_DB_PATH="/app/db/custom.db"
DEFAULT_PACKAGED_DATABASE_URL="file:$DEFAULT_PACKAGED_DB_PATH"
# 启动 Next.js 服务器
if [ -f "./next-service-dist/server.js" ]; then
echo "🚀 启动 Next.js 服务器..."
cd next-service-dist/ || exit 1
# 设置环境变量
export NODE_ENV=production
export PORT="${PORT:-3000}"
export HOSTNAME="${HOSTNAME:-0.0.0.0}"
export DATABASE_URL="${DATABASE_URL:-$DEFAULT_PACKAGED_DATABASE_URL}"
if [ "$DATABASE_URL" = "$DEFAULT_PACKAGED_DATABASE_URL" ]; then
if [ ! -f "$DEFAULT_PACKAGED_DB_PATH" ]; then
echo "❌ 未找到打包后的数据库文件 $DEFAULT_PACKAGED_DB_PATH"
echo " 为避免生产环境启动到空数据库,启动已终止"
exit 1
fi
echo "🗄️ 当前使用打包数据库: $DEFAULT_PACKAGED_DB_PATH"
else
echo "🗄️ 当前使用外部指定数据库: $DATABASE_URL"
fi
# 后台启动 Next.js
bun server.js &
NEXT_PID=$!
pids="$NEXT_PID"
# 等待一小段时间检查进程是否成功启动
sleep 1
if ! kill -0 "$NEXT_PID" 2>/dev/null; then
echo "❌ Next.js 服务器启动失败"
exit 1
else
echo "✅ Next.js 服务器已启动 (PID: $NEXT_PID, Port: $PORT)"
fi
cd ../
else
echo "⚠️ 未找到 Next.js 服务器文件: ./next-service-dist/server.js"
fi
# 启动 mini-services
if [ -f "./mini-services-start.sh" ]; then
echo "🚀 启动 mini-services..."
# 运行启动脚本(从根目录运行,脚本内部会处理 mini-services-dist 目录)
sh ./mini-services-start.sh &
MINI_PID=$!
pids="$pids $MINI_PID"
# 等待一小段时间检查进程是否成功启动
sleep 1
if ! kill -0 "$MINI_PID" 2>/dev/null; then
echo "⚠️ mini-services 可能启动失败,但继续运行..."
else
echo "✅ mini-services 已启动 (PID: $MINI_PID)"
fi
elif [ -d "./mini-services-dist" ]; then
echo "⚠️ 未找到 mini-services 启动脚本,但目录存在"
else
echo "️ mini-services 目录不存在,跳过"
fi
# 启动 Caddy(如果存在 Caddyfile
echo "🚀 启动 Caddy..."
# Caddy 作为前台进程运行(主进程)
echo "✅ Caddy 已启动(前台运行)"
echo ""
echo "🎉 所有服务已启动!"
echo ""
echo "💡 按 Ctrl+C 停止所有服务"
echo ""
# Caddy 作为主进程运行
exec caddy run --config Caddyfile --adapter caddyfile
-1
View File
File diff suppressed because one or more lines are too long
-1
View File
File diff suppressed because one or more lines are too long
Executable
+23
View File
@@ -0,0 +1,23 @@
:81 {
@transform_port_query {
query XTransformPort=*
}
handle @transform_port_query {
reverse_proxy localhost:{query.XTransformPort} {
header_up Host {host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
}
}
handle {
reverse_proxy localhost:3000 {
header_up Host {host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
}
}
}
Executable
+196
View File
@@ -0,0 +1,196 @@
# 回响星核 / Echo Nexus
> 原创深空考古 **放置策略**游戏 · Original deep-space archaeology **idle strategy** game
你是一名深空考古指挥官,驾驶自治勘探舰回收已「飞升」的以太文明遗留的记忆晶体,解码碎片拼凑出他们的故事,并最终跨越维度发起「接触」。
**关掉浏览器,星核仍在为你工作**——采矿无人机舰队 24h 不间断采矿,放置工程在离线时持续推进,回来后一键领取积累的奖励。
---
## 🎮 在线游玩
**👉 [https://gitea-pages.atdunbg.xyz/Super_Z/echo-nexus/](https://gitea-pages.atdunbg.xyz/Super_Z/echo-nexus/)**
无需安装,打开即玩。支持桌面(鼠标 + 键盘)与移动端(触控)。
---
## 项目状态
- **版本****v0.15** — 叙事深化(loreLayers 多层碎片文本)+ 放置平衡(永久加成 softcap)+ 批量领取 + 完成 toast
- **栈**Next.js 16 (App Router) · TypeScript · Tailwind CSS 4 · shadcn/ui · Canvas 2D · Zustand · Web Audio API
- **仓库**[https://git.atdunbg.xyz/Super_Z/echo-nexus](https://git.atdunbg.xyz/Super_Z/echo-nexus)
- **在线版**[https://gitea-pages.atdunbg.xyz/Super_Z/echo-nexus/](https://gitea-pages.atdunbg.xyz/Super_Z/echo-nexus/)
- **部署**Gitea Pages(静态导出,`BUILD_EXPORT=true next build``out/` 推送 `gh-pages` 分支)
---
## 核心特色
- **📡 采矿无人机舰队**:每解锁一个采矿分支技术,就有一台自动采矿无人机加入舰队,24h 持续产出晶体(无需点击)
- **⚙️ 放置工程项目**:6 种工程,派遣后**离线/在线自动推进**,完成领取奖励(晶体/洞见/能量/永久产能加成/随机碎片)
- **🔓 离线收益**:关闭浏览器后产能按 `offlineEff × crystalsPerSec` 持续累积(上限 8h),重新打开弹窗结算「沉睡期间」收益
- **🎯 始终可见的放置徽章**:Header 永远显示「放置中 +X/s」徽章,悬停查看产能构成明细——一眼就知道这是个 idle game
- **🔍 解码共振谜题**:把放置游戏的「等待」变成有节奏的主动小谜题(路径构造法生成器,100% 可解)
- **📜 碎片涌现叙事**:故事从解码中自然浮现,5 纪元循环命名 + 模板化 lore,不打断玩法
- **💎 深空全息美学**:晶体 + Canvas 粒子 + 共振波纹的差异化视觉(严格 emerald/rose/amber/fuchsia 四色全息色谱,零蓝色/靛色)
- **🔄 飞升多周目**:终局「接触」后保留蓝图开启新周目,星图天文台 18 个天赋跨周目永久生效
- **🚀 实时巡航玩法**Canvas 2D 飞船操控 + 8 种实体 + 3 层视差星空,打破纯放置节奏
- **🧬 角色 RPG 深度**:四维属性(探索力/智慧/勇气/灵感)影响所有游戏系统
---
## 游戏系统(11 大系统)
| # | 系统 | 简介 | 类型 |
|---|------|------|------|
| 1 | **📡 放置采矿** | 中央晶体球 Canvas 粒子系统 + 主动脉冲 + 连击加成 + **自动采矿无人机舰队** | 放置 |
| 2 | **⚙️ 放置工程** | 6 种工程 × 3 槽位,**离线/在线自动推进**,完成领取奖励 | **放置** |
| 3 | **🔓 离线收益** | 关闭浏览器后产能按 `eff × cps` 累积(8h 上限),重新打开结算 | **放置** |
| 4 | 谐振解码 | 原创路径谜题,100% 可解率 | 主动 |
| 5 | 技术树 | 三层科技解锁,提升产能/解码/探险 | 主动 |
| 6 | 遗迹探险 | 肉鸽系统,6 种节点 + 程序化路径 + BOSS | 主动 |
| 7 | 星潮事件 | 6 种动态事件,全屏叠层 + 程序化音效 | 被动 |
| 8 | 星图天文台 | 飞升后 3 选 1 天赋 draft,18 个跨周目天赋 | 元进程 |
| 9 | 回响编年史 | 跨周目叙事时间轴,5 纪元循环命名 | 元进程 |
| 10 | 深空信标 | 每日挑战 + 本地排行榜 Top20 | 每日 |
| 11 | 深空巡航 | Canvas 2D 实时玩法,飞船操控/陨石躲避/星门通关 | 实时 |
| 12 | 角色属性 | 四维属性(探索力/智慧/勇气/灵感),飞升获得属性点 | 元进程 |
---
## 放置系统设计哲学(v0.14 + v0.15 平衡)
回响星核的「放置」不是简单地把数字挂机增长,而是分层呈现 **3 种 idle 节奏**
### 节奏 1:被动产能(最慢,最持续)
- 采矿无人机舰队 = 你已解锁的采矿技术节点
- 每台无人机 24h 持续产出晶体,无需任何操作
- 晶体进入仓库(受 `crystalCap` 限制),仓库满后停止产出(防溢出)
- **永远在线,离线也累积**
### 节奏 2:放置工程(中等周期,需要决策)
- 6 种工程,时长 1min ~ 1h 不等
- 玩家选择派遣到 3 个槽位之一 → 启动后**离线/在线自动推进**
- 完成后必须**手动领取**(防玩家错过奖励时机)
- 奖励类型多样:晶体 / 洞见 / 能量 / 永久产能加成 / 随机碎片
- **核心 idle loop:派遣 → 离开 → 回来 → 领取 → 再派遣**
### 节奏 3:离线收益(最长周期,回归奖励)
- 关闭浏览器后,被动产能按 `offlineEff`(默认 50%,可通过技术提升至 100%)继续累积
- 上限 8 小时(防极端挂机)
- 重新打开页面弹窗结算:「你离开了 X 小时,星核为你产出 Y 颗晶体」
- **回归玩家的"啊我回来了"瞬间**
---
## 开发路线
| 版本 | 核心内容 | 状态 |
|------|---------|------|
| v0.1 | 放置 + 解码 + 技术树 + 图谱 + 飞升 | ✅ |
| v0.1.1 | 解码可解性修复(路径构造法) | ✅ |
| v0.2 | 遗迹探险肉鸽系统 | ✅ |
| v0.2.1 | 程序化音频 + 14 项成就 | ✅ |
| v0.3 | 星潮事件系统 | ✅ |
| v0.3.1 | 星图天文台元进程 | ✅ |
| v0.4 | 回响编年史系统 | ✅ |
| v0.5 | 深空信标系统 | ✅ |
| v0.5.1 | Gitea Pages 静态部署 + 新手教程 | ✅ |
| v0.5.2 | 离线收益报告 + 标签页 UI 重设计 | ✅ |
| v0.6 | 深空巡航 Canvas 2D 实时玩法 | ✅ |
| v0.7 | CrystalOrb Canvas 粒子系统 + 四维角色属性 | ✅ |
| v0.8 | 巡航玩法增强 / BOSS 关卡 / 道具掉落 | ✅ |
| v0.8.1 | 探险平衡 + 信标系统扩展(周挑战 + 信标链) | ✅ |
| **v0.14** | **放置系统:采矿无人机舰队 + 6 种放置工程 + 永久产能加成 + 始终可见 idle 徽章** | ✅ |
| **v0.15** | **叙事深化(8 碎片 × 2 层 = 16 层 loreLayers+ 永久加成 softcap + 批量领取 + 完成 toast + StatsPanel idle 统计** | ✅ |
| v0.16 | 解码阈值释放叙事碎片 + 星潮三频段切换 | 🔜 |
| v0.17 | 健康设计:每日软上限 + "星核建议休眠"提醒 | 🔜 |
| v1.0 | 全 5 纪元手写叙事 + 多人同步星潮 | 🔜 |
---
## 文档
- [`docs/01-调研报告.md`](docs/01-调研报告.md) — 游戏市场调研
- [`docs/02-游戏设计文档.md`](docs/02-游戏设计文档.md) — GDD 策划设计
- [`docs/03-技术架构.md`](docs/03-技术架构.md) — 技术架构
- [`docs/04-解码系统修复-v0.1.1.md`](docs/04-解码系统修复-v0.1.1.md) — v0.1.1 解码修复
- [`docs/05-遗迹探险系统-v0.2.md`](docs/05-遗迹探险系统-v0.2.md) — v0.2 探险系统
- [`docs/06-音频与成就系统-v0.2.1.md`](docs/06-音频与成就系统-v0.2.1.md) — v0.2.1 音频与成就
- [`docs/07-星潮事件系统-v0.3.md`](docs/07-星潮事件系统-v0.3.md) — v0.3 星潮
- [`docs/08-星图天文台系统-v0.3.1.md`](docs/08-星图天文台系统-v0.3.1.md) — v0.3.1 天文台
- [`docs/09-回响编年史系统-v0.4.md`](docs/09-回响编年史系统-v0.4.md) — v0.4 编年史
- [`docs/10-深空信标系统-v0.5.md`](docs/10-深空信标系统-v0.5.md) — v0.5 信标
- [`docs/11-放置系统-v0.14.md`](docs/11-放置系统-v0.14.md) — **v0.14 放置系统(idle operations**
- [`docs/12-叙事深化-v0.15.md`](docs/12-叙事深化-v0.15.md) — **v0.15 loreLayers 多层碎片文本 + softcap + 批量领取**
---
## 本地开发
```bash
# 安装依赖
bun install
# 启动 dev 服务器(http://localhost:3000
bun run dev
# 静态导出(用于 Gitea Pages 部署)
BUILD_EXPORT=true bun run build:static
# 产物在 out/ 目录,basePath 为 /Super_Z/echo-nexus
# 代码检查
bun run lint
```
---
## 部署到 Gitea Pages
```bash
# 1. 静态导出
BUILD_EXPORT=true bun run build:static
# 2. 推送到 gh-pages 分支
cd out
git init && git checkout -b gh-pages
git add -A
git commit -m "deploy: v0.15"
git remote add origin https://git.atdunbg.xyz/Super_Z/echo-nexus.git
git push -f origin gh-pages
```
Gitea Pages 会自动从 `gh-pages` 分支提供静态网站服务。
---
## 灵感来源(Inspired by
- **Antimatter Dimensions** — 数值膨胀美学 + 终端 UI
- **Universal Paperclips** — 叙事驱动的放置游戏(Frank Lantz
- **A Dark Room** — 极简文字放置游戏(Michael Townsend
- **Melvor Idle** — 离线收益 + 多技能树
- **Outer Wilds** — 知识解锁式考古
- **Cookie Clicker** — Orteil 的放置游戏范式定义
---
## 健康游戏设计声明
放置游戏天然具有"低投入高回报"的成瘾性,可能对学业/工作压力大的青年造成时间碎片化风险。回响星核主动采取以下健康设计:
-**离线收益上限 8h**——防止 24h 挂机
-**工程完成需手动领取**——避免"打开就有奖励"的条件反射
-**无广告、无内购**——纯粹的单机体验
- 🔜 **v0.17 计划**:每日软上限 + "星核建议休眠"提醒(每 1 小时主动提示休息)
参考学术研究:
- Frank Lantz GDC 2018 "This Is Your Brain on Games"
- Reuters 2025 "Cozy video games can quell stress and anxiety"
- DOI:10.3389/fpsyg.2026.1774030 TikTok 时代学术倦怠研究
---
© Super_Z · 持续开发中
-9
View File
@@ -1,9 +0,0 @@
1:"$Sreact.fragment"
2:I[47257,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ClientPageRoot"]
3:I[52683,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/efcc875da619c164.js"],"default"]
6:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"OutletBoundary"]
7:"$Sreact.suspense"
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/efcc875da619c164.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
4:{}
5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
8:null
-22
View File
@@ -1,22 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
4:I[77855,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js"],"Toaster"]
5:I[47257,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ClientPageRoot"]
6:I[52683,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/efcc875da619c164.js"],"default"]
9:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"OutletBoundary"]
a:"$Sreact.suspense"
c:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ViewportBoundary"]
e:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"MetadataBoundary"]
10:I[68027,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/media/797e433ab948586e-s.p.29207c2f.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
:HL["/Super_Z/echo-nexus/_next/static/media/caa3a2e1cccd8315-s.p.3b6cae6d.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
0:{"P":null,"b":"oyCsV_kYxc3pie8zrYRGs","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","suppressHydrationWarning":true,"className":"dark","children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased bg-background text-foreground","children":[["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L4",null,{}]]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/efcc875da619c164.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$10",[]],"S":true}
7:{}
8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
11:I[27201,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"IconMark"]
b:null
f:[["$","title","0",{"children":"回响星核 · Echo Nexus"}],["$","meta","1",{"name":"description","content":"原创深空考古放置策略游戏 — 解码记忆晶体,拼凑以太文明的回响,跨越维度发起接触。"}],["$","meta","2",{"name":"author","content":"Super_Z"}],["$","meta","3",{"name":"keywords","content":"Echo Nexus,回响星核,idle game,browser game,放置游戏,解谜,深空考古"}],["$","meta","4",{"property":"og:title","content":"回响星核 · Echo Nexus"}],["$","meta","5",{"property":"og:description","content":"原创深空考古放置策略游戏"}],["$","meta","6",{"property":"og:site_name","content":"Echo Nexus"}],["$","meta","7",{"property":"og:type","content":"website"}],["$","meta","8",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","9",{"name":"twitter:title","content":"回响星核 · Echo Nexus"}],["$","meta","10",{"name":"twitter:description","content":"原创深空考古放置策略游戏"}],["$","link","11",{"rel":"icon","href":"https://z-cdn.chatglm.cn/z-ai/static/logo.svg"}],["$","$L11","12",{}]]
-6
View File
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ViewportBoundary"]
3:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"MetadataBoundary"]
4:"$Sreact.suspense"
5:I[27201,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"IconMark"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"回响星核 · Echo Nexus"}],["$","meta","1",{"name":"description","content":"原创深空考古放置策略游戏 — 解码记忆晶体,拼凑以太文明的回响,跨越维度发起接触。"}],["$","meta","2",{"name":"author","content":"Super_Z"}],["$","meta","3",{"name":"keywords","content":"Echo Nexus,回响星核,idle game,browser game,放置游戏,解谜,深空考古"}],["$","meta","4",{"property":"og:title","content":"回响星核 · Echo Nexus"}],["$","meta","5",{"property":"og:description","content":"原创深空考古放置策略游戏"}],["$","meta","6",{"property":"og:site_name","content":"Echo Nexus"}],["$","meta","7",{"property":"og:type","content":"website"}],["$","meta","8",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","9",{"name":"twitter:title","content":"回响星核 · Echo Nexus"}],["$","meta","10",{"name":"twitter:description","content":"原创深空考古放置策略游戏"}],["$","link","11",{"rel":"icon","href":"https://z-cdn.chatglm.cn/z-ai/static/logo.svg"}],["$","$L5","12",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
-7
View File
@@ -1,7 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
4:I[77855,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js"],"Toaster"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","precedence":"next"}],["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","async":true}],["$","script","script-1",{"src":"/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","async":true}]],["$","html",null,{"lang":"zh-CN","suppressHydrationWarning":true,"className":"dark","children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased bg-background text-foreground","children":[["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}],["$","$L4",null,{}]]}]}]]}],"loading":null,"isPartial":false}
-5
View File
@@ -1,5 +0,0 @@
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/media/797e433ab948586e-s.p.29207c2f.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
:HL["/Super_Z/echo-nexus/_next/static/media/caa3a2e1cccd8315-s.p.3b6cae6d.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-2
View File
@@ -1,2 +0,0 @@
@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/fef07dbb0973bf53-s.518e079e.woff2)format("woff2");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/8a480f0b521d4e75-s.ea323500.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/53b9e256198e5412-s.853d50a3.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/7178b3e590c64307-s.55554cd0.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/caa3a2e1cccd8315-s.p.3b6cae6d.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Geist Fallback;src:local(Arial);ascent-override:95.94%;descent-override:28.16%;line-gap-override:0.0%;size-adjust:104.76%}.geist_a71539c9-module__T19VSG__className{font-family:Geist,Geist Fallback;font-style:normal}.geist_a71539c9-module__T19VSG__variable{--font-geist-sans:"Geist","Geist Fallback"}
@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/5ce348bf30bf5439-s.56c1f21e.woff2)format("woff2");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/4fa387ec64143e14-s.3b336396.woff2)format("woff2");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/6306c77e7c8268e4-s.e3369375.woff2)format("woff2");unicode-range:U+2000-2001,U+2004-2008,U+200A,U+23B8-23BD,U+2500-259F}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/7d817b4c03b0c5f1-s.a40b9a8b.woff2)format("woff2");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/bbc41e54d2fcbd21-s.fe42ddf4.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(../media/797e433ab948586e-s.p.29207c2f.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Geist Mono Fallback;src:local(Arial);ascent-override:74.67%;descent-override:21.92%;line-gap-override:0.0%;size-adjust:134.59%}.geist_mono_8d43a2aa-module__8Li5zG__className{font-family:Geist Mono,Geist Mono Fallback;font-style:normal}.geist_mono_8d43a2aa-module__8Li5zG__variable{--font-geist-mono:"Geist Mono","Geist Mono Fallback"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
@@ -1 +0,0 @@
[]
@@ -1 +0,0 @@
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
-16
View File
@@ -1,16 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
4:I[77855,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"Toaster"]
5:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"OutletBoundary"]
6:"$Sreact.suspense"
8:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ViewportBoundary"]
a:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"MetadataBoundary"]
c:I[68027,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
0:{"P":null,"b":"oyCsV_kYxc3pie8zrYRGs","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","suppressHydrationWarning":true,"className":"dark","children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased bg-background text-foreground","children":[["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L4",null,{}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
d:I[27201,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"IconMark"]
7:null
b:[["$","title","0",{"children":"回响星核 · Echo Nexus"}],["$","meta","1",{"name":"description","content":"原创深空考古放置策略游戏 — 解码记忆晶体,拼凑以太文明的回响,跨越维度发起接触。"}],["$","meta","2",{"name":"author","content":"Super_Z"}],["$","meta","3",{"name":"keywords","content":"Echo Nexus,回响星核,idle game,browser game,放置游戏,解谜,深空考古"}],["$","meta","4",{"property":"og:title","content":"回响星核 · Echo Nexus"}],["$","meta","5",{"property":"og:description","content":"原创深空考古放置策略游戏"}],["$","meta","6",{"property":"og:site_name","content":"Echo Nexus"}],["$","meta","7",{"property":"og:type","content":"website"}],["$","meta","8",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","9",{"name":"twitter:title","content":"回响星核 · Echo Nexus"}],["$","meta","10",{"name":"twitter:description","content":"原创深空考古放置策略游戏"}],["$","link","11",{"rel":"icon","href":"https://z-cdn.chatglm.cn/z-ai/static/logo.svg"}],["$","$Ld","12",{}]]
-6
View File
@@ -1,6 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ViewportBoundary"]
3:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"MetadataBoundary"]
4:"$Sreact.suspense"
5:I[27201,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"IconMark"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"回响星核 · Echo Nexus"}],["$","meta","1",{"name":"description","content":"原创深空考古放置策略游戏 — 解码记忆晶体,拼凑以太文明的回响,跨越维度发起接触。"}],["$","meta","2",{"name":"author","content":"Super_Z"}],["$","meta","3",{"name":"keywords","content":"Echo Nexus,回响星核,idle game,browser game,放置游戏,解谜,深空考古"}],["$","meta","4",{"property":"og:title","content":"回响星核 · Echo Nexus"}],["$","meta","5",{"property":"og:description","content":"原创深空考古放置策略游戏"}],["$","meta","6",{"property":"og:site_name","content":"Echo Nexus"}],["$","meta","7",{"property":"og:type","content":"website"}],["$","meta","8",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","9",{"name":"twitter:title","content":"回响星核 · Echo Nexus"}],["$","meta","10",{"name":"twitter:description","content":"原创深空考古放置策略游戏"}],["$","link","11",{"rel":"icon","href":"https://z-cdn.chatglm.cn/z-ai/static/logo.svg"}],["$","$L5","12",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
-7
View File
@@ -1,7 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
4:I[77855,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"Toaster"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","precedence":"next"}],["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","async":true}],["$","script","script-1",{"src":"/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","async":true}],["$","script","script-2",{"src":"/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js","async":true}]],["$","html",null,{"lang":"zh-CN","suppressHydrationWarning":true,"className":"dark","children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased bg-background text-foreground","children":[["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}],["$","$L4",null,{}]]}]}]]}],"loading":null,"isPartial":false}
@@ -1,5 +0,0 @@
1:"$Sreact.fragment"
2:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"OutletBoundary"]
3:"$Sreact.suspense"
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
4:null
-4
View File
@@ -1,4 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
-3
View File
@@ -1,3 +0,0 @@
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
0:{"buildId":"oyCsV_kYxc3pie8zrYRGs","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
File diff suppressed because one or more lines are too long
-16
View File
@@ -1,16 +0,0 @@
1:"$Sreact.fragment"
2:I[39756,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
3:I[37457,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
4:I[77855,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"Toaster"]
5:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"OutletBoundary"]
6:"$Sreact.suspense"
8:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"ViewportBoundary"]
a:I[97367,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"MetadataBoundary"]
c:I[68027,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"default"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","style"]
:HL["/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","style"]
0:{"P":null,"b":"oyCsV_kYxc3pie8zrYRGs","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/34d933785a17edf3.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/Super_Z/echo-nexus/_next/static/chunks/f4a1ac847b63088d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","suppressHydrationWarning":true,"className":"dark","children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased bg-background text-foreground","children":[["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L4",null,{}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:0:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
d:I[27201,["/Super_Z/echo-nexus/_next/static/chunks/2d46f11719722bdc.js","/Super_Z/echo-nexus/_next/static/chunks/62cf09ef1d4f1188.js","/Super_Z/echo-nexus/_next/static/chunks/33d3f2c0cda968c7.js"],"IconMark"]
7:null
b:[["$","title","0",{"children":"回响星核 · Echo Nexus"}],["$","meta","1",{"name":"description","content":"原创深空考古放置策略游戏 — 解码记忆晶体,拼凑以太文明的回响,跨越维度发起接触。"}],["$","meta","2",{"name":"author","content":"Super_Z"}],["$","meta","3",{"name":"keywords","content":"Echo Nexus,回响星核,idle game,browser game,放置游戏,解谜,深空考古"}],["$","meta","4",{"property":"og:title","content":"回响星核 · Echo Nexus"}],["$","meta","5",{"property":"og:description","content":"原创深空考古放置策略游戏"}],["$","meta","6",{"property":"og:site_name","content":"Echo Nexus"}],["$","meta","7",{"property":"og:type","content":"website"}],["$","meta","8",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","9",{"name":"twitter:title","content":"回响星核 · Echo Nexus"}],["$","meta","10",{"name":"twitter:description","content":"原创深空考古放置策略游戏"}],["$","link","11",{"rel":"icon","href":"https://z-cdn.chatglm.cn/z-ai/static/logo.svg"}],["$","$Ld","12",{}]]
+67
View File
@@ -0,0 +1,67 @@
# Task 2-b · full-stack-developer · v0.15 叙事深化 — Fragment loreLayers 多层文本
## 上下文 / 接收到的任务
- Task ID: **2-b**
- 任务: 为 Fragment 增加 loreLayersL1 中层 / L2 深层),实现 Dark Souls 式碎片叙事
- 必读上下文已加载(worklog v0.14-reimpl 段落 + FRAGMENTS 数组 + Fragment 接口 + Codex.tsx + gameStore 导出)
- 与并行子代理 A 的边界:不动 engine.ts / gameStore.ts / IdleOperationsPanel.tsx / StatsPanel.tsx / idle.ts / persist version
## 修改的文件(绝对路径)
1. `/home/z/my-project/src/lib/game/types.ts`
2. `/home/z/my-project/src/lib/game/config.ts`
3. `/home/z/my-project/src/components/game/Codex.tsx`
## 实现细节
### types.ts
- 新增 `FragmentLoreUnlockKind` 联合类型(5 种解锁维度)
- 新增 `FragmentLoreUnlock` 接口(kind + value
- 新增 `FragmentLoreLayer` 接口(layer 1|2 + title + text + unlockHint + unlock
- `Fragment` 接口新增可选字段 `loreLayers?: FragmentLoreLayer[]`
- 现有 `echo` 字段注释明确为 "L0 表层文本",保持向后兼容
### config.ts
- 顶部 import 扩展 `FragmentLoreUnlock` + `GameState` 类型
- **为全部 8 个 FRAGMENTS 撰写了 loreLayers**(≥6 目标超额完成,每碎片 2 层)
- 解锁条件分布:
- ascensions 1 ×2 (f1_1, f1_4) / 2 ×2 (f1_3, f1_6) / 3 ×2 (f1_7, f1_8)
- totalDecoded 40 ×2 (f1_1, f1_7) / 60 ×1 (f1_4)
- contact 50 ×1 (f1_2) / 80 ×2 (f1_5, f1_8)
- fragmentCount 4 ×1 (f1_2) / 6 ×1 (f1_5)
- bossKills 3 ×1 (f1_3) / 8 ×1 (f1_6)
- 新增导出函数 `isLoreLayerUnlocked(unlock, state)` 纯函数判定
- GameState 通过 `Pick<>` 类型约束,只取需要的 5 字段(ascensions / totalDecoded / contact / fragments / bossKills
### 叙事暗线设计(5 条贯穿主线)
1. **追逐者线** — 以太族不是自然消亡,是被虚空中的"追逐者"逼迫集体飞升(f1_1, f1_2, f1_5, f1_8
2. **回响 AI 同源线** — 玩家舰载 AI 的频段直接复制自以太族议会档案,玩家即是以太族的延续/载体(f1_1, f1_6)
3. **"信任"即"利用"线** — 第一纪元的"信任后来者"实为"引诱后来者完成未竟之事"f1_4, f1_8
4. **飞升代价线** — 飞升不是奖赏而是单向门 + 替以太族还债(f1_4, f1_7, f1_8
5. **满溢回归线** — 接触进度满 80 时回归的不是"他们",是"借他们形状的某种东西"——警示玩家(f1_8)
### Codex.tsx 重构
- 用 `useShallow` 一次性订阅 5 字段(fragments / totalDecoded / ascensions / contact / bossKills
- 顶部新增"深层叙事 X / Y 已解锁"统计行(amber 数字 + mono 字体)
- 列表卡片:当某碎片有 loreLayer 已解锁时,标题旁显示彩色小圆点(L1 amber / L2 fuchsia),带 glow shadow
- 详情弹层重构为分层渲染:
- **L0 表层回响**(永远显示,emerald 主题,bg-emerald-950/20
- **L1 隐秘注脚**(amber 主题,未解锁时乱码占位 + blur-sm + 🔒 unlockHint
- **L2 深层回响**(fuchsia 主题,未解锁时同样乱码占位 + blur-sm + 🔒 unlockHint
- 锁定层用 `▓░▒` 全息方块字 + `blur-sm select-none pointer-events-none aria-hidden` 保密
- 弹层 Card 加 `max-h-[88vh] overflow-y-auto` + 自定义 scrollbar 颜色(fuchsia
- 每层之间用 `border-t border-white/5` 细分隔线
- 移动端 375px 适配:text-[12px] 行距宽松,padding p-3,长 unlockHint 在极窄屏隐藏(`hidden xs:inline sm:inline`
## 验证
- ✅ `bun run lint` 零错误零警告(首次有 1 个 unused eslint-disable 警告,已修复)
- ✅ dev 服务器 HTTP 200,编译 142-257ms 范围
- ✅ dev.log 末尾连续多次"✓ Compiled"无运行时错误
- ✅ 严格 4 色全息:emerald / amber / fuchsia / roserose 仅在背景 used);零蓝/靛
- ✅ useShallow 用于多字段订阅
- ✅ 未修改 persist version / engine / gameStore(与子代理 A 零冲突)
## 需要主控注意的点
1. **loreLayers 已为全部 8 个碎片填充**(不止 6 个),叙事密度最高。如果主控希望平衡节奏,可考虑给 1-2 个"基础"碎片只留 L0(去掉 loreLayers 字段即可,UI 会自动显示"此碎片暂无更深层叙事"占位)。
2. **未做端到端 agent-browser 验证**(按工单第 5 步要求,由主控完成)。建议主控验证:① 初始进入无飞升状态,所有 L1/L2 应处于锁定态(模糊乱码 + 🔒 图标);② 用 `window.__gameStore.setState({ascensions:1})` 模拟飞升后,f1_1 / f1_4 的 L1 应自动解锁;③ 列表卡片标题旁应出现 amber/fuchsia 小圆点。
3. **未持久化 loreLayer 解锁进度**——这是纯派生状态(由现有 5 字段实时计算),无需新增持久化字段。如主控希望加"已读"标记(防止玩家错过新解锁的深层文本),可后续在 GameState 加 `readLoreLayers: Record<string, boolean>`,但需要改 gameStore(与子代理 A 边界冲突),故本轮未做。
4. 叙事文本长度 60-130 字,符合"碎片感 + 留白"美学要求;没有说教,多用引号 + 反转 + 留白。
+52
View File
@@ -0,0 +1,52 @@
# Task 6: 开发 Canvas 2D 深空巡航实时玩法
## Agent: full-stack-developer
## 完成状态: ✅ 全部完成
## 创建的文件
1. `/home/z/my-project/src/lib/game/cruise.ts` — 逻辑层(~520 行纯 TS
2. `/home/z/my-project/src/components/game/CruiseMode.tsx` — Canvas 2D 渲染层(~830 行)
## 修改的文件
1. `/home/z/my-project/src/app/page.tsx` — 新增巡航入口按钮 + CruiseMode 渲染
## 关键实现细节
### cruise.ts 逻辑层
- **类型系统**CruiseEntity 联合类型(8 种实体:ship/asteroid/storm/crystal/insight/beacon/stargate/particle),完整 CruiseState/Phase/RunResult/Stats/Input
- **关卡生成**mulberry32 + FNV-1a 种子化 RNG;陨石(8+level×2)、风暴(1+level/2)、晶体(5+level)、洞见(2+level/3)、信标(1+level/4)、星门;时长 60-90s
- **物理**:飞船 8 方向加速度+摩擦+限速+边界反弹;陨石碰撞扣盾+击退+600ms 无敌;风暴持续 DPS;收集物吸引半径 90px 自动吸入
- **奖励**crystals=碎片×(8+lvl×2)、insights=洞见×(2+lvl×0.5)、contact=信标×(1.5+lvl×0.3);通关×1.5、失败×0.5
- **localStorage**:独立 key `echo-nexus-cruise-v1`,记录最高分/通关数/最高关卡/累计奖励/最近 20 局
### CruiseMode.tsx 渲染层
- **全屏 Canvas**fixed inset-0 z-50DPR cap 2resize 监听
- **3 层视差星空**:远/中/近 100/60/30 星,独立漂移+飞船速度视差+闪烁
- **实体辉光绘制**:全部使用 ctx.shadowBlur;飞船三角形+尾焰渐变;陨石不规则多边形;风暴 5 层云团+闪电;晶体菱形+脉冲环;洞见球体+漩涡;信标光束+旋转环;星门 4 层旋涡+吸入粒子
- **粒子系统**:尾焰/收集/碰撞/烟花,上限 200
- **屏幕震动**:受击 280ms 衰减抖动
- **低护盾警告**shield<30% 屏幕边缘 rose 脉冲边框
- **HUD**HTML 叠层(非 canvas),glass+backdrop-blur;护盾/能量/分数/用时/收集计数;80ms 节流更新
- **控制**:桌面 WASD/方向键;移动端 floating 虚拟摇杆;P 暂停;Esc 退出;失焦自动暂停
- **奖励同步**:结算时 grantCruiseReward + recordRunrewardGrantedRef 防重
### page.tsx 集成
- header 按钮区新增「巡航」按钮(Navigation 图标,amber 主题)
- 新增 cruiseOpen state
- 组件末尾渲染 {cruiseOpen && <CruiseMode onClose={...} />}
- 未破坏现有 7 标签页和其他功能
## QA 验证结果
- ✅ lint 零错误
- ✅ 巡航按钮出现在顶部 header
- ✅ 点击进入全屏 Canvas,ready 界面完整
- ✅ WASD 控制飞船移动,按 W 直冲星门通关
- ✅ 收集 2 晶体 → 奖励 +30(2×10×1.5=30 计算正确)
- ✅ localStorage 正确记录 highScore/totalWins/bestLevel
- ✅ 奖励同步到 gameStore
- ✅ 退出返回主界面,7 标签页完好
- ✅ 移动端 viewport 测试通过
## 色彩遵循
emerald(#34d399) / rose(#fb7185) / amber(#fbbf24) / fuchsia(#e879f9) 四色全息色谱,零蓝色/靛色
+103
View File
@@ -0,0 +1,103 @@
# Task 7: 角色属性系统(探索力/智慧/勇气/灵感)
## Agent: full-stack-developer
## 完成状态: ✅ 全部完成
## 创建的文件
1. `/home/z/my-project/src/lib/game/attributes.ts` — 属性逻辑层(~330 行)
2. `/home/z/my-project/src/components/game/AttributesPanel.tsx` — 属性 UI 面板(~330 行)
## 修改的文件
1. `/home/z/my-project/src/lib/game/types.ts` — GameState 新增 attributes/attributeProgress/pendingAttrPoints
2. `/home/z/my-project/src/lib/game/config.ts` — INITIAL_STATE 补全新字段默认值
3. `/home/z/my-project/src/lib/game/engine.ts` — recomputeStats 聚合属性加成;performPrestige 发放属性点
4. `/home/z/my-project/src/store/gameStore.ts` — 新增 allocateAttribute/gainAttributeExp actionpulse/clickNode/autoDecodeTick/resolveCurrentNode/grantCruiseReward/tickTide/init 接入属性逻辑;旧存档兼容
5. `/home/z/my-project/src/lib/game/achievements.ts` — 新增 2 项属性成就
6. `/home/z/my-project/src/app/page.tsx` — 新增第 8 个「角色」标签页 + grid-cols-7→8 + 红点提示 + 统计面板新增属性行 + 版本号 v0.6→v0.7
## 关键实现细节
### attributes.ts 逻辑层
- **类型系统**AttributeKey4 个键)/ CharacterAttributes0-100 数值)/ AttributeProgressEntryexp + level
- **加成公式**
- 0-50 线性区:每点 +0.5% 加成(0..50 → 0..25%
- 50-100 递减区:每点 +0.2% 加成(50..100 → 25..35%
- 超过 100 仍按 100 计算加成(软上限)
- **getAttributeBonus(attr)** 单属性加成百分比
- **getAllBonuses(attrs)** 返回 12 个修饰器:探险力倍率/巡航速度/解码步数/洞见倍率/自动解码周期/探险生命/BOSS 胜率/巡航护盾/接触率/星潮触发/脉冲连击/产能加成
- **levelUpCheck(progress)** 自动跨多级升级,安全上限 200 次循环
- **expRequiredForLevel(level) = 10 × level**(最低 10
- **computePrestigeAttrPoints(ascensions) = ascensions × 2 + 1**(飞升前次数计算)
- **migrateAttributes(state)** 旧存档兼容:补全缺失字段、夹紧越界值、同步 level 与 attributes
### engine.ts 改造
- recomputeStats 末尾追加 `getAllBonuses(state.attributes ?? {})` 聚合:
- `crystalsPerSec *= am.crystalsPerSecMult`
- `insightMult += am.insightMultAdd`
- `contactRateMult *= am.contactRateMult`
- `decodeStepsBonus += am.decodeStepsBonus`
- performPrestige
- 保留 attributes 数值(跨周目永久)
- 清空 attributeProgress(新周目重新累积经验)
- pendingAttrPoints += computePrestigeAttrPoints(ascensions) = ascensions × 2 + 1
- createInitialState:每次返回全新 attributes/attributeProgress 对象,避免引用共享
### gameStore.ts 集成(核心)
- **pulse**:连击 ≥3 给灵感经验(expGain = 1 + floor(combo/2));脉冲威力乘以灵感连击加成
- **clickNode** 完成:给智慧经验(expGain = puzzle.tier × 2
- **autoDecodeTick**:自动解码也给智慧经验 +1;自动解码周期受智慧 am.autoDecodeIntervalMult 影响
- **startExpedition**:探险力乘 am.expeditionPowerMult;探险生命加 am.expeditionHpBonus
- **resolveCurrentNode**
- BOSS 节点用包装 RNG 提升 +am.bossWinRateBonus 胜率(单次 rng 调用,B% 概率返回 0,其余返回 r-B 保持均匀分布)
- 战斗胜利给勇气+探索力经验(中途战斗 +2/+1,BOSS 击破 +8/+6,探险胜利 +4
- **grantCruiseReward**:按总奖励量缩放给探索力+勇气经验(expBase = max(2, totalReward/30)
- **tickTide**:灵感 am.tideTriggerBonus 缩短星潮间隙(gap × (1 - bonus)),上限 30%
- **doPrestige**performPrestige 后用 next.attributes 重算 stats
- **allocateAttribute(attr, points=1)**:分配属性点,同步 attributeProgress[attr].level
- **gainAttributeExp(attr, amount)**:通用经验获取(自动升级)
- **init()**:调用 migrateAttributes 补全旧存档字段,并传 attributes 到 syncStats
- 所有 syncStats 调用点(10+ 处)都补充 `attributes: ...` 参数
### AttributesPanel.tsx UI
- 四维属性卡片网格(小屏 2×2,大屏 1×4):
- 图标(Compass/Brain/Swords/Sparkles+ 中文名 + 英文名 + Lv.{value} badge
- 数值 /100 + 加成百分比
- 经验进度条(gradient + glow+ "递减区"标记
- 加成影响列表(3 条)
- 「+分配」按钮(pendingAttrPoints > 0 时可点,hover scale 105
- 顶部:标题 + 待分配点数 badgependingAttrPoints > 0 时 echo-pending-pulse 动画)
- 底部:总等级 + 总加成概览 + 12 个修饰器明细行
- 配色:4 色全息(emerald/fuchsia/amber/rose),辉光边框 + 顶角光晕装饰
- 完全响应式(2 列 → 4 列),overflow-y-auto + 自定义 scrollbar
### page.tsx 集成
- 新增 `User` 图标导入
- 新增 `pendingAttrPoints` store selector
- TabsList: grid-cols-7 → grid-cols-8
- 新增第 8 个 TabsTrigger「角色」(value="attributes"),主题色用 emerald→fuchsia→rose 渐变
- pendingAttrPoints > 0 时显示 rose 红点
- 新增 TabsContent 渲染 AttributesPanel
- StatsPanel 新增 5 行属性行(探索力/智慧/勇气/灵感/待分配属性点)
- 版本号 v0.6 → v0.7
### achievements.ts 新增
- `ach_attr_total_50`(四维觉醒):四维属性总和 ≥ 50 → 产能+6%/洞见+6%
- `ach_attr_max_100`(维度精通):任一属性 ≥ 100 → 产能+12%/洞见+10%
## QA 验证结果
- ✅ `bun run lint` 零错误
- ✅ dev 服务器 HTTP 200
- ✅ 编译 < 250ms177ms 实测)
- ✅ 7 标签页 + 巡航按钮完好保留,新增第 8 个「角色」标签页
- ✅ data-tut 锚点(tab-expedition/tab-tech/prestige-btn/crystal-orb/decode-panel)保留
- ✅ 色彩规范:严格 emerald/fuchsia/amber/rose 四色全息,零蓝色/靛色
- ✅ 旧存档兼容:migrateAttributes 补全 attributes/attributeProgress/pendingAttrPoints 字段
- ✅ 飞升后 pendingAttrPoints = ascensions × 2 + 1(飞升前次数)
- ✅ pulse/clickNode/resolveCurrentNode/grantCruiseReward/autoDecodeTick 均接入属性经验获取
## 注意事项
- BOSS 胜率 RNG 包装:仅在 boss 节点生效,单次 rng 调用保持均匀分布;+am.bossWinRateBonus 上限 +30%
- 灵感星潮触发:通过缩短 gap 间接提升触发频率(上限 30%)
- 属性加成与现有所有系统(技术树/蓝图/成就/星图/星潮)叠加,不冲突
- migrateAttributes 同步 attributeProgress[attr].level 与 attributes[attr] 数值,避免漂移
+141
View File
@@ -0,0 +1,141 @@
# Task 9-b · full-stack-developer · 信标系统扩展(周挑战 + 信标链)
> 本文件为本 agent 在 Task 9-b 的工作记录,供后续 agent 查阅。
## 任务概述
为「回响星核 / Echo Nexus」v0.8 扩展深空信标系统,新增两大功能:
1. **周挑战(Weekly Challenge** — 每周一 UTC 0 点刷新,目标更大、奖励更好,与日挑战并行
2. **信标链(Beacon Chain** — 连续完成日挑战形成"链",达成里程碑(3/7/14/30 天)领取递增大奖,含 grace 续命机制
## 阅读的前置工作
- `/home/z/my-project/worklog.md`(v0.8 项目状态,10 大系统,四色全息规范)
- `/home/z/my-project/src/lib/game/beacon.ts`(v0.5 原版 358 行:每日挑战 + 本地排行榜)
- `/home/z/my-project/src/components/game/BeaconPanel.tsx`(原版 301 行)
- `/home/z/my-project/src/store/gameStore.ts` 第 173 行 `trackBeacon` 函数 + 7 处调用点(pulse/clickNode/autoDecodeTick/resolveCurrentNode 等)
## 实现细节
### 1. beacon.ts 扩展(358 → 873 行)
**周挑战(WEEKLY CHALLENGEsection**
- `BeaconWeeklyChallenge` 接口 + `BeaconWeeklyProgress` 接口
- `getWeekKey(now)`ISO 8601 周键(YYYY-Www,周一为起点,含首个周四的周为第一周)
- `weekKeyToSeed`FNV-1a 哈希
- `generateWeeklyChallenge(now)`:基于 weekKey 种子确定性生成
- 难度加权:anomaly 60% / singular 40%
- mult = 3 + floor(rng() * 3) → 3-5 倍
- goal 范围:decode 18-75 / expedition 3-15 / pulse 60-245 / boss 3-12 / insight 120-595
- `loadWeeklyProgress` / `saveWeeklyProgress` / `addWeeklyProgress` / `claimWeeklyReward`
- `msUntilNextWeek(now)`
- `BEACON_WEEKLY_KEY = "echo-nexus-beacon-weekly-v1"`
**信标链(BEACON CHAINsection**
- `BeaconChainState` 接口(lastCompletedDateKey / currentStreak / longestStreak / totalCompletions / graceUsed / milestonesClaimed
- `BEACON_CHAIN_KEY = "echo-nexus-beacon-chain-v1"`
- `BEACON_CHAIN_MILESTONES = [3, 7, 14, 30] as const`
- `BEACON_CHAIN_REWARDS`4 个里程碑
- 3 天:+50 洞见 / +5 接触 / "三日谐振"
- 7 天:+120 洞见 / +12 接触 / "七日回响"
- 14 天:+280 洞见 / +28 接触 / "半月星潮"
- 30 天:+680 洞见 / +68 接触 / "满月飞升"
- `loadChainState` / `saveChainState`(每次返回新对象避免引用共享 bug
- `recordChainCompletion(dateKey)` 核心逻辑:
- 同日重复完成 → 忽略
- 次日 → streak++
- 隔一天 miss 且 graceUsed<1 → 续命 streak++ graceUsed++
- 其他 → 断链 streak=1 graceUsed=0
- 返回 `{ state, newMilestones }`
- `claimChainMilestone(milestone)` / `getNextMilestone(streak)` / `getChainProgress(streak)`
- `dateKeyToTimestamp` / `dateKeyDiffDays` 工具
`BeaconScoreEntry` 新增可选 `isWeekly?: boolean` 字段,向后兼容。
### 2. gameStore.ts 集成
- import 扩展:新增 generateWeeklyChallenge / loadWeeklyProgress / loadChainState / addWeeklyProgress / recordChainCompletion / claimWeeklyReward / claimChainMilestone / getTodayKey + 类型
- `trackBeacon(type, delta)` 返回值从 `boolean` 升级为 `{ dailyJustCompleted, weeklyJustCompleted, newChainMilestones }`
- 同时更新日挑战进度 + 周挑战进度
- 日挑战刚完成时调用 `recordChainCompletion(getTodayKey())`
- 所有 7 处调用点原本忽略返回值,向后兼容
- 新增 action `claimWeeklyBeacon()`:调用 claimWeeklyReward → 发放奖励到 state
- 新增 action `claimChainReward(milestone)`:前置校验 → claimChainMilestone → 发放奖励
- GameActions 接口同步扩展
### 3. BeaconPanel.tsx 重写(301 → 638 行)
- 头部 + 每日挑战卡片(v0.5 保留)+ 难度色按钮主题
- **周挑战区块**fuchsia 主题):标题 + weekKey + 倒计时 + 卡片(标签/标题/描述/进度/奖励/领取按钮 emerald+ weekly-glow 动画
- **信标链区块**amber→rose 渐变):
- 标题"信标链 · CHAIN" + 大字号 streakchain-streak-text 流动渐变动画)
- 今日完成状态徽章
- 4 个里程碑节点(w-12 h-12 圆形):
- claimed: emerald 实心 + ✓
- reachable: rose 脉冲动画 + "领取"按钮
- inProgress (next milestone): amber 半亮
- 未到达: muted 灰
- 节点间连线:背景灰 + 已达成部分 amber→rose 渐变 + 辉光,基于 prev→next 插值定位
- 进度条 + 底部统计(最长链/累计完成/续命状态)
- 桌面端 lg:grid-cols-2 让周挑战 + 信标链并排,移动端单列
- 排行榜区分日/周:周挑战 entry 显示 "WEEK" 徽章 + fuchsia 高亮背景
- 4 个新 CSS 动画:weekly-glow / chain-milestone-pulse / chain-streak-flux
## QA 验证结果
### 1. lint
- `bun run lint` 零错误(每次修改后均验证)
### 2. dev 服务器
- dev.log 全程无错误,所有编译 < 300msHTTP 200
### 3. 信标链逻辑测试(bun 直接运行 TS,5 个场景全 PASS)
1. ✅ 昨日 streak=1 → 今日完成 → streak=2normal increment
2. ✅ 同日重复完成 → 忽略
3. ✅ 明日完成 → streak=3,无需 grace
4. ✅ 隔一天 misslastCompleted + 2 天)→ streak=4graceUsed=1(续命触发)
5. ✅ 再次 missgrace 已用)→ 断链 streak=1graceUsed=0
### 4. UI 集成测试(agent-browser
- localStorage 模拟设置 chain statelastCompletedDateKey=yesterday, currentStreak=1)→ reload → UI 正确显示 streak=1
- 设置 streak=3 + milestonesClaimed=[] → milestone 3 节点显示 rose 脉冲动画 + "领取"按钮
- 点击"领取"→ milestonesClaimed=[3] + Toast "✦ 三日谐振 已领取 +50 洞见 · +5.0 接触" + 按钮消失 ✅
### 5. VLM 视觉评分(目标 ≥7/10)
- 首屏截图:**8/10**(四色一致、布局合理、信标链清晰)
- 里程碑可领取状态:**8/10**(amber→rose 渐变醒目、里程碑层次分明、fuchsia 主题清晰)
- 重置后干净状态:**8/10**(WEEKLY 字体对比度可优化,但整体可读性强)
## 修改的文件
1. `src/lib/game/beacon.ts` — 358 → 873 行(+515 行)
2. `src/store/gameStore.ts` — trackBeacon 升级 + 2 个新 action+~80 行)
3. `src/components/game/BeaconPanel.tsx` — 301 → 638 行(+337 行,重写)
## 不破坏的现有功能
- 日挑战进度追踪与领奖流程
- 本地排行榜 Top20
- 现有 5 种挑战类型 + 3 档难度
- 现有 localStorage keysecho-nexus-beacon-lb-v1 / echo-nexus-beacon-prog-v1
## 新增的 localStorage keys
- `echo-nexus-beacon-weekly-v1`(周挑战进度)
- `echo-nexus-beacon-chain-v1`(信标链状态)
## 四色全息规范遵循
- 周挑战主题:**fuchsia** (#e879f9)
- 信标链主题:**amber → rose** 渐变 (#fbbf24#fb7185)
- 领取按钮:**emerald** (#34d399)
- 难度色:routine emerald / anomaly amber / singular rose
- **零蓝色/靛色违规**
## 截图资产
- `/home/z/my-project/agent-ctx/beacon-panel-v0.8.png` — 初次进入信标页
- `/home/z/my-project/agent-ctx/beacon-chain-streak1.png` — chain streak=1 状态
- `/home/z/my-project/agent-ctx/beacon-chain-milestone3.png` — chain streak=3 milestone 可领取
- `/home/z/my-project/agent-ctx/beacon-milestone3-claimable.png` — milestone 3 领取前
- `/home/z/my-project/agent-ctx/beacon-final-fresh.png` — 重置后干净状态
Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

+139
View File
@@ -0,0 +1,139 @@
# Task ID: v0.14-reimpl
# Agent: full-stack-developer
# Task: 放置系统 (Idle Operations) — 采矿无人机舰队 + 6放置工程 + 永久产能加成 + idle徽章
## Work Log
### 1. 类型扩展 (`src/lib/game/types.ts`)
- 新增 `IdleProjectDef` 接口(id/name/desc/durationSec/reward/minAscensions/minCrystalsPerSec/icon/color/order
- 新增 `IdleProjectSlot` 接口(projectId/startedAt/finishesAt/remainingSec/completed
- 在 `GameState` 接口 `pendingAttrPoints` 后新增 4 个字段:
- `idleProjectSlots: (IdleProjectSlot | null)[]`
- `idleProjectHistory: { projectId: string; finishedAt: number }[]`
- `idlePermanentBonus: number`
- `idleStats: { projectsCompleted: number; crystalsFromIdle: number }`
### 2. 新建 `src/lib/game/idle.ts` (~290 行)
- 定义 6 个放置工程(按 order 排序):
| id | name | duration | reward | icon | color | unlock |
|----|------|----------|--------|------|-------|--------|
| idle_scan | 深空勘探扫描 | 60s | insights +8 | 🛰️ | emerald | cps≥0.5 |
| idle_refine | 晶体精炼阵列校准 | 180s | permBonus +0.3/s | ⚙️ | rose | cps≥1 |
| idle_archive | 遗迹碎片整理 | 600s | randomFragment + insights +20 | 📜 | amber | asc≥1 OR cps≥5 |
| idle_anchor | 维度锚点部署 | 1200s | energy +2 + contact +5 | ⚓ | fuchsia | asc≥1 |
| idle_drones | 无人机群扩编 | 1800s | permBonus +2/s + crystals +500 | 🛸 | emerald | cps≥10 |
| idle_resonance | 跨维度谐振标定 | 3600s | permBonus +8/s + contact +15 + insights +100 | 🌌 | fuchsia | asc≥2 |
- 导出:`IDLE_PROJECTS`, `IDLE_SLOT_COUNT=3`, `getIdleProject`, `getUnlockedIdleProjects`, `getLockedIdleProjects`, `isIdleProjectUnlocked` (OR 逻辑:满足任一已设置条件即可解锁), `formatRemaining` (12s/3m 45s/1h 12m), `formatDuration`, `formatReward`, `deriveMinerFleet` (派生 min_1..min_5 采矿无人机,无任何采矿技术时返回休眠 min_1)
- 导出颜色映射 `IDLE_COLOR_CLASSES` (text/border/bg/bgSoft/glow/ring/dot) + `COLOR_HEX` (用于 inline style,绕过 Tailwind 动态类限制)
### 3. 引擎集成 (`src/lib/game/engine.ts`)
- `recomputeStats()` 末尾 `crystalsPerSec += state.idlePermanentBonus ?? 0` (放置永久加成叠加到产能)
- `performPrestige()` 在返回对象中:
- 清空 `idleProjectSlots: [null, null, null]` (飞升中断当前周目放置工程)
- 保留 `idleProjectHistory`, `idlePermanentBonus`, `idleStats` (跨周目永久)
- `recomputeStats` 调用传入 `idlePermanentBonus` 以确保飞升后立即应用永久加成
- `createInitialState()` 新增 4 字段默认值
### 4. Store actions (`src/store/gameStore.ts`)
- 新增 4 个 action
- `startIdleProject(slotIndex, projectId)`: 校验槽位空 + 项目已解锁 + 未在他处运行;写入 `{ projectId, startedAt, finishesAt, remainingSec, completed:false }`
- `cancelIdleProject(slotIndex)`: 置槽位为 null
- `claimIdleProject(slotIndex)`: 校验已完成 → 应用奖励(crystals 限 crystalCapenergy 限 energyMaxcontact 限 100permBonus 累加到 idlePermanentBonus + 重算 statsrandomFragment 从未解锁碎片中随机一个)→ 追加 history (slice -50) → idleStats.projectsCompleted++ → Toast 提示
- `tickIdleProjects(now)`: 遍历非空非完成槽位,更新 remainingSec,归零时标记 completed
- **persist migrate 函数**persist 配置增加 `version: 1` + `migrate` 函数,自动补全 idleProjectSlots/idleProjectHistory/idlePermanentBonus/idleStats 4 字段,旧存档加载不崩溃
- 全部 14 处 syncStats 调用更新为传入 `idlePermanentBonus: s.idlePermanentBonus ?? 0`,确保 recomputeStats 计算时纳入永久加成
- `init()` 中也补全 4 个 idle 字段(defense in depth
### 5. 主循环 (`src/hooks/useGameLoop.ts`)
- 新增 `tickIdleProjects` 选择器
- setInterval 回调中 `autoDecodeTick()` 后调用 `tickIdleProjects(now)`
- visibilitychange 回调中也调用 `tickIdleProjects(now)`
### 6. 新建 `src/components/game/IdleOperationsPanel.tsx` (~490 行)
- 主「放置」标签内容,4 个 section 在 `max-h-[520px] overflow-y-auto` 容器中:
1. **放置收益概览**3 stat tiles (放置产能 / 永久加成 / 完成工程) + 离线效率 progress bar + 累计放置晶体统计
2. **采矿无人机舰队**grid 展示 deriveMinerFleet(state),每张卡:emoji + name + Lv + output/s + 状态点(active=emerald ping 脉冲,dormant=灰)
3. **放置工程槽位**:3 张槽位卡(空槽=虚线占位,运行中=大字号倒计时+自定义进度条+取消按钮,已完成=奖励预览+领取按钮带 glow 辉光)
4. **可派遣工程**:2 列 grid 展示已解锁工程(icon+name+duration+desc+reward+3 个数字派遣按钮 1/2/3),下方列出未解锁工程及解锁条件
- 使用 `useShallow` 订阅多个 store 字段
- 本地 `now` state 每 1s 刷新倒计时
- 严格 4 色全息(emerald/rose/amber/fuchsia),无蓝/靛
- 自定义滚动条样式 (fuchsia 主题)
### 7. 新建 `src/components/game/IdleStatusBadge.tsx` (~100 行)
- 紧凑徽章:脉冲点 (emerald 若 cps>0,否则灰) + "放置中 +X/s" (或 "休眠中")
- Tooltip 悬停展示分解:基础产能 / 永久加成 / 运行中工程 / 待领取工程数
- `h-8 px-2.5 text-[11px]`,点击切换到放置标签
- `data-tut="idle-status-badge"` 锚点
### 8. 新建 `src/components/game/IdleProjectBar.tsx` (~90 行)
- 晶体球下方的细长进度条
- 显示最多 3 个运行/已完成工程为 mini pill:`[icon] name 12s ▓▓▓░░`
- 完成时显示 "✓ 待领取" + glow 辉光
- 无工程时返回 null
- 本地 `now` state 每 1s 刷新倒计时
- 进度条颜色用 inline style 控制(COLOR_HEX 映射)
### 9. 主页面集成 (`src/app/page.tsx`)
- 引入 3 个新组件 + `Clock` 图标 + `getUnlockedIdleProjects`
- 新增 `idleProjectSlots` / `createdAt` store 订阅
- 新增 `activeTab` / `tabInited` statecontrolled Tabs`<Tabs value={activeTab} onValueChange={setActiveTab}>`
- 挂载后 useEffect 一次性设置默认 tabhasActiveExpedition → "expedition"hasPendingPerk → "constellation",否则 → "idle"
- 版本号 v0.8 → v0.14
- Header 在 StarTideIndicator 后加 IdleStatusBadge
- 左侧 CrystalOrb 后加 IdleProjectBar
- TabsList grid-cols-8 → grid-cols-9,新增 `value="idle"` 的 TabsTrigger 作为第一个 tabClock 图标,emerald 主题,待领取时显示数量红点)
- 新增 TabsContent value="idle" 渲染 IdleOperationsPanel
- 新增 idle 相关 goal 提示(高优先级):
- 待领取 > 0 → "✦ 放置工程已完成 X 项,请前往「放置」标签领取奖励"
- 全空 + 有解锁 + 时长 > 60s → "「放置」标签可派遣工程项目,离线自动产出"
- StatsPanel 新增 3 行:放置永久加成 / 完成放置工程 / 放置产出晶体
### 10. 教程更新 (`src/lib/game/tutorial.ts`)
- TUTORIAL_STEPS 在 decode 与 tech 之间插入新步骤:
- id: "idle", target: "tab-idle", placement: "top"
- 标题 "③ 放置工程 · 离线产出"
- 介绍放置标签的工程派遣 + 顶部徽章 + 晶体球下方进度条
- 原 tech/expedition/prestige 步骤编号顺延为 ④⑤⑥
## QA 验证
### Lint
- `bun run lint` → 零错误零警告 ✅
### HTTP
- `curl http://localhost:3000/` → 200 OK ✅
### Dev log
- 全程无 runtime error,所有请求 200 OK,编译 < 250ms ✅
### agent-browser 烟雾测试
1. ✅ 版本标签 "v0.14" 可见
2. ✅ 默认 tab 是 "放置"data-state="active" 在第一个 tab
3. ✅ 头部 idle 徽章可见("放置中 +0.4/s" 或 "休眠中"
4. ✅ IdleOperationsPanel 4 个 section 全部可见(放置收益概览 / 采矿无人机舰队 / 放置工程槽位 / 可派遣工程 / 未解锁工程)
5. ✅ 采矿无人机舰队在无采矿技术时显示休眠 min_1(emoji ⛏️ + 灰色状态点)
6. ✅ 设置 insights=50 → buyTech('min_1') → crystalsPerSec 0.4→1.0 → 解锁 idle_scan + idle_refine
7. ✅ 点击 idle_scan 派遣按钮 → 槽位 0 写入 { projectId: "idle_scan", finishesAt: now+60000 } → 倒计时正确递减
8. ✅ 60s 后自动标记 completed=true → 槽位卡显示 "✓ 完成" + 领取按钮
9. ✅ 点击领取 → Toast "✦ 工程奖励已领取 · 深空勘探扫描 · +8 洞见" → insights 8→16 → history 追加 → idleStats.projectsCompleted 0→1
10. ✅ 派遣 idle_refine (180s) → 强制完成 → 领取 → crystalsPerSec 1.0→1.3 → idlePermanentBonus 0→0.3 ✅ 永久产能加成正确应用
11. ✅ IdleProjectBar 在晶体球下方显示:`🛰️ 深空勘探扫描 55s ▓▓▓░░`
12. ✅ Footer goal 文字根据状态动态切换为放置相关提示
13. ✅ Footer 产能显示 "产能 1.3/s" (含 idle 永久加成)
### Migration 测试
- 旧存档(无 idle 字段)通过 migrate 函数自动补全为默认值,加载不崩溃 ✅
## Stage Summary
- ✅ 类型层:新增 IdleProjectDef + IdleProjectSlot + 4 GameState 字段
- ✅ 逻辑层:新建 idle.ts (~290 行),6 工程定义 + 派生函数 + 颜色映射
- ✅ 引擎层:recomputeStats / performPrestige / createInitialState 全部接入 idle 永久加成
- ✅ Store 层:4 新 action + persist migrate (version 1) + 14 处 syncStats 调用全部传 idlePermanentBonus
- ✅ 主循环:tickIdleProjects 接入 setInterval + visibilitychange
- ✅ UI 层:3 新组件 (IdleOperationsPanel ~490 行 / IdleStatusBadge ~100 行 / IdleProjectBar ~90 行)
- ✅ 主页面:默认放置 tab + 头部徽章 + 晶体球下方进度条 + 版本 v0.14 + 9 标签页
- ✅ 教程:新增 idle 步骤 (③ 放置工程)
- ✅ 严格 4 色全息 (emerald/rose/amber/fuchsia),零蓝/靛
- ✅ lint 零错误 + HTTP 200 + agent-browser 全流程烟雾测试通过
- ✅ 5 秒内可见三要素:头部徽章 + 默认放置 tab + 晶体球下方进度条(派遣后)
Executable
+1965
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
Executable
BIN
View File
Binary file not shown.
+95
View File
@@ -0,0 +1,95 @@
# 游戏市场调研报告
> 调研日期:2025 年(持续更新)
> 调研者:Super_Z
> 目的:为原创游戏《回响星核 / Echo Nexus》的设计提供数据支撑,避免落入「1:1 复刻」陷阱。
---
## 一、市场大盘(数据时效性已核实,多源交叉验证)
### 1.1 浏览器 / HTML5 游戏市场
- **市场规模**2025 年浏览器游戏市场约 **78 亿 ~ 118 亿美元**(不同机构口径略有差异),预计 2033-2034 年达到 **92 亿 ~ 163 亿美元**CAGR 约 3.5%。
- 来源:Research and Markets78.1 亿→80.1 亿)、Dataintelo118 亿→163 亿)、Business Research Insights。
- **HTML5 游戏细分**2024 年约 53.2 亿美元,2033 年预计 92.2 亿美元,CAGR 约 7.7%LinkedIn / Metastat)。
- **供给爆发**2025 年有 **15,000+** 款 HTML5 新作上线,同比增长 **2.7 倍**Business Research Insights)。
- **技术趋势**WebAssembly 让浏览器游戏性能逼近原生;WebGL/WebGPU 渲染能力持续提升。
**结论**:浏览器游戏是增长赛道,**可及性高、潜在受众巨大(尤其国际市场)**,但供给端竞争激烈,**必须靠差异化创新突围**。
### 1.2 独立游戏品类格局(Steam 视角)
- **2025 年叙事游戏**成为 Steam 达到 1000 评价数量最多的品类(#1),超越传统品类(HowToMarketAGame 年度报告)。
- **新一代独立原生品类**:开放世界生存、卡牌构筑(deckbuilder)、Roguelike 持续走强(SuperJump)。
- **红海/饱和品类**(Reddit 独开者共识,建议避开正面对撞):Roguelike Deckbuilder、视觉小说、本地合作、4X。
- **2025 独立代表作**Hades II、Deltarune、Ways of Alchemy、Cluckmech Oasis 等。
**结论**:纯复刻热门品类(如又一款肉鸽卡牌)会被淹没;**叙事 + 创新机制组合**是突破口。
### 1.3 放置 / 增量(Idle / Incremental)品类
- **极强留存**:Idle 类天然契合浏览器场景,代表作 Cookie Clicker、Universal Paperclips、Melvor Idle 长盛不衰。
- **2025 热度持续**GameSpot、Bits N' Pixels 年度榜单均大量收录 idle/clicker 游戏。
- **典型玩法**:资源自动累积 + 主动操作 + 解锁/重置(prestige)循环。
- **优势**:开发成本可控、留存曲线平缓、适合持续迭代。
### 1.4 玩家留存关键洞察
- **核心循环(core loop**是留存的根基;D1/D7/D30 是关键基准。
- **渐进式功能发现**:让玩家逐步解锁新机制,避免一次性信息过载。
- **社交连接 + 流畅运营**:长期留存的两大支柱。
- **动机分层**:成就型、探索型、社交型玩家需要差异化设计。
---
## 二、竞品分析与差异化机会
| 维度 | 主流放置游戏 | 主流叙事游戏 | 主流肉鸽 | 《回响星核》定位 |
|---|---|---|---|---|
| 核心 | 数值增长 | 剧情推进 | 战斗随机 | **解码谜题 + 放置 + 探险** |
| 叙事 | 弱/无 | 强但线性 | 弱 | **碎片化叙事,由玩法涌现** |
| 美术 | 简笔画/像素 | 插画 | 像素 | **全息晶体 + 深空粒子美学** |
| 留存钩子 | prestige | 章节 | 随机性 | **「接触」终局 + 多周目** |
| 单局 | 长 | 中 | 短 | **可长可短(放置 + 主动)** |
**差异化结论**
1. 没有任何一款主流游戏同时是「深空考古 + 记忆解码谜题 + 放置 + 肉鸽探险 + 碎片叙事」。
2. 叙事由解码行为**涌现**而非过场灌输,符合 2025 叙事品类崛起趋势。
3. 放置核心保证留存,解码谜题提供主动乐趣,探险提供随机性重玩价值。
---
## 三、技术选型调研结论
- **框架**Next.js 16App Router+ TypeScript —— 本项目既有栈,SSR/路由/API 一体化,适合 web 游戏。
- **渲染**2D 场景用 **HTML5 Canvas**(星空/粒子/晶体动画),UI 用 **React + shadcn/ui**。3D 暂不需要,控制复杂度。
- **状态**Zustand(客户端)+ localStorage 持久化;后续可接 API 同步存档。
- **后端**Next.js API Routes + Prisma(SQLite),承载存档同步、全局统计、(后续)排行榜。
- **实时**:socket.io 小服务,用于全局「星潮」事件(后续阶段)。
**结论**:在既有 Next.js 栈内即可实现,无需引入重框架,性能与可维护性平衡良好。
---
## 四、设计原则(源自调研)
1. **原创优先**:不做任何现有游戏的 1:1 复刻,机制组合与世界观均自创。
2. **核心循环扎实**:放置产出 → 解码消耗 → 解锁成长 → 探险扩张 → 终局「接触」。
3. **叙事涌现**:故事从解码碎片中拼出,不打断玩法。
4. **渐进披露**:功能随进度解锁,符合留存最佳实践。
5. **视觉差异化**:全息晶体 + 深空粒子,第一眼即与同类区分。
6. **可持续迭代**:模块化架构,便于后续通过工单需求持续扩展。
---
## 五、数据来源清单(真实性/时效性核验)
1. Business Research Insights — Browser Games Market Report
2. Research and Markets — Browser Games Market 2026
3. Dataintelo — Browser Game Market Research 2034
4. Metastat Insights — HTML5 Games Market 2030
5. LinkedIn (行业分析) — Casual HTML5 Games 2024-2033
6. HowToMarketAGame — 2025 Steam 品类年度报告
7. SuperJump — State of Indie Games 2025
8. GameSpot / Bits N' Pixels — 2025 Idle 游戏榜单
9. GameDesignSkills / GameDeveloper — 留存策略 17 条
10. Reddit r/incremental_games、r/gamedev 社区共识
所有数据均为 2024-2026 年发布,时效性满足设计决策需要。
+141
View File
@@ -0,0 +1,141 @@
# 《回响星核 / Echo Nexus》游戏设计文档 (GDD)
> 版本:v0.1(首发概念版)
> 作者:Super_Z
> 状态:设计已定稿,进入实现阶段
---
## 一、一句话概念
**你是一名深空考古指挥官,驾驶自治勘探舰回收已「飞升」的以太文明遗留的记忆晶体,解码碎片拼凑出他们的故事,并最终跨越维度发起「接触」。**
## 二、游戏定位
- **品类**:深空考古放置策略 + 记忆解码谜题 + 肉鸽探险 + 涌现叙事
- **平台**:现代浏览器(桌面优先,移动适配)
- **单局时长**:可长可短(放置 30 秒也有产出;主动游玩 10-30 分钟一轮探险)
- **目标留存**D1 > 40%D7 > 15%idle 品类基准之上)
- **差异化**:业界首个「解码谜题驱动叙事涌现」的放置游戏
## 三、世界观
数万年前,银河系存在一个高度发达的文明——**以太族(Aetherians)**。他们没有灭亡,而是集体「飞升」到了更高维度。临走前,他们将文明的知识与记忆封存在遍布星系的**记忆晶体(Memory Crystals)**中,作为留给后来者的「面包屑」——只待某个文明能解读足够多的晶体,便有资格发起「接触」。
玩家扮演人类深空考古局的指挥官,舰载 AI「回响(Echo)」辅助你勘探遗迹、回收晶体、解码记忆。
## 四、核心循环
```
┌─────────────────────────────────────────────┐
▼ │
[自治无人机采矿] ──产出──> [记忆晶体库存] │
│ │
[解码谜题] <── 主动操作 │
│ │
┌──────────────┼──────────────┐ │
▼ ▼ ▼ │
[记忆碎片] [技术洞见] [剧情片段] │
(叙事资源) (解锁升级) (拼凑故事) │
│ │ │ │
└──────────────┴──────────────┘ │
│ │
[发起遗迹探险] ─肉鸽─> 新晶体类型
│ │
[「接触」进度条 ↑] │
│ │
────── 达成 100% ───── 终局 / 新周目
```
**核心循环一句话**:采矿→解码→成长→探险→填满接触条→新周目。
## 五、核心系统
### 5.1 记忆晶体采集(Idle 核心)
- 自治无人机以 **晶体/秒** 速率自动产出记忆晶体。
- 玩家可主动「脉冲扫描」(点击/按键)获得瞬时增量 + 连击加成。
- 离线时按 50% 效率持续产出(最多累积 8 小时)。
### 5.2 解码系统(主动玩法核心 ⭐)
每颗晶体需经「解码」才能释放其中内容。解码是一个**原创的图案共振谜题**:
- 屏幕呈现一个由发光节点构成的**共振阵列**(类似星图)。
- 玩家需在限定步数内,按特定**共振顺序**点亮节点,使能量流闭合形成回路。
- 成功 → 释放记忆碎片 + 技术洞见 + 剧情片段。
- 不同晶体等级 / 类型 → 阵列规模与共振规则不同(渐进难度)。
- **设计意图**:把「解码」从被动等待变成有节奏的主动小谜题,避免放置游戏「只挂机不动手」的枯燥。
### 5.3 技术树(成长系统)
四个分支,由「技术洞见」解锁:
- **采矿**:提升晶体/秒、仓库上限、离线效率。
- **解码**:降低解码步数消耗、解锁阵列提示、自动解码低级晶体。
- **探险**:提升探险成功率、降低风险、增加稀有晶体掉落。
- **叙事**:解锁更深记忆层、揭示隐藏剧情、提升「接触」进度转化率。
### 5.4 遗迹探险(Roguelike 层)
- 消耗「能量」向随机生成的遗迹节点图派出探险队。
- 每个节点有事件(战斗/解谜/宝藏/抉择),路径分支。
- 失败损失探险队,成功带回稀有晶体 + 大量洞见 + 独有剧情。
- 单次探险 10-30 分钟,提供短期目标与随机性重玩价值。
### 5.5 碎片叙事(叙事层)
- 解码与探险产出的「记忆碎片」自动归档到**记忆图谱(Memory Codex**。
- 图谱以星图形式呈现,碎片拼合后揭示以太族编年史(5 个纪元)。
- 关键碎片触发**「回响回放」**——一段沉浸式全息短叙(文字 + 视觉),但**绝不打断玩法**(可随时关闭)。
### 5.6 「接触」终局与多周目
- 全部行为累积「接触进度」。
- 达 100% → 触发终局「接触」事件(戏剧性叙事高潮)。
- 之后可选择**飞升(Prestige)**进入新周目:保留叙事图谱与技术蓝图的一部分,重置数值,开启更高难度与隐藏内容。
## 六、美术与音频方向
- **视觉**:深空黑紫底色 + 全息青/品虹光晶体 + 流动星尘粒子。整体「冷峻神秘 + 科技仪式感」。
- **字体**:标题用未来感无衬线,正文清晰可读。
- **动效**:晶体生长、解码共振波纹、粒子涌动、飞升白光。
- **音频**:环境氛围 pad + 解码成功清脆音 + 关键叙事轻旋律(后续阶段)。
## 七、UI 信息架构
```
顶栏:晶体库存 | 接触进度 | 能量 | 设置
左侧:主视图(Canvas 星空 + 晶体集群 + 解码阵列)
右侧:标签面板
- 采矿 (Mining)
- 解码 (Decode) ⭐默认
- 技术 (Tech)
- 探险 (Expedition)
- 记忆图谱 (Codex)
底栏:当前目标提示 | 在线/离线收益 | 版本号
```
## 八、首版(v0.1MVP 范围
为快速验证核心乐趣,首版聚焦:
- [x] 放置采矿(晶体/秒 + 主动脉冲)
- [x] 解码共振谜题(3 种阵列规模)
- [x] 技术树 4 分支 × 3 级 = 12 个升级
- [x] 记忆图谱(首纪元 8 个碎片)
- [x] 接触进度 + 飞升(Prestige
- [x] localStorage 存档 + 离线收益
- [x] 精美 Canvas 星空背景 + 晶体动画
- [x] 响应式布局(桌面/移动)
**v0.1 暂不做**:遗迹探险肉鸽、socket 全局事件、音频、多语言切换、账号云存档(留待后续版本/工单驱动)。
## 九、后续路线图
- **v0.2**:遗迹探险肉鸽系统
- **v0.3**:音频系统 + 全局「星潮」socket 事件
- **v0.4**:账号云存档 + 排行榜
- **v0.5**:多周目隐藏内容 + 全 5 纪元叙事
- **持续**:根据工单需求迭代
## 十、设计风险与对策
| 风险 | 对策 |
|---|---|
| 解码谜题单调 | 多种阵列规则 + 自动解码低级晶体 |
| 放置枯燥 | 主动脉冲 + 连击 + 探险提供短期目标 |
| 叙事打断玩法 | 所有叙事可跳过,图谱自愿浏览 |
| 数值膨胀 | 飞升重置 + 指数曲线校准 |
| 移动端操作 | 解码阵列适配触控,UI 自适应 |
+109
View File
@@ -0,0 +1,109 @@
# 技术架构设计
## 一、技术栈
| 层 | 技术 | 说明 |
|---|---|---|
| 框架 | Next.js 16 (App Router) | 既有栈,SSR + API 一体 |
| 语言 | TypeScript 5 | 全量类型 |
| 样式 | Tailwind CSS 4 + shadcn/ui | 既有 UI 组件库 |
| 渲染 | HTML5 Canvas 2D | 星空/粒子/晶体/解码阵列 |
| 客户端状态 | Zustand + persist | 游戏状态 + localStorage 持久化 |
| 服务端状态 | TanStack Query(按需) | 存档同步、统计 |
| 后端 | Next.js API Routes | 存档同步、全局统计 |
| 数据库 | Prisma + SQLite | 存档、统计(v0.4+ |
| 实时 | socket.io mini-service | 全局「星潮」事件(v0.3+) |
## 二、目录结构
```
src/
app/
page.tsx # 游戏主入口(唯一用户可见路由)
layout.tsx
api/
save/route.ts # 存档同步(v0.4+
stats/route.ts # 全局统计
components/
game/ # 游戏专属组件
StarfieldCanvas.tsx # 星空背景
CrystalCluster.tsx # 晶体集群
DecodeArray.tsx # 解码共振阵列 ⭐
ResourceBar.tsx # 顶栏资源
MiningPanel.tsx
TechTree.tsx
Codex.tsx # 记忆图谱
PrestigeDialog.tsx
ui/ # shadcn/ui 既有
lib/
game/ # 游戏逻辑(纯函数,可测)
config.ts # 数值配置
types.ts # 类型
engine.ts # 核心 tick 引擎
decode.ts # 解码谜题逻辑
tech.ts # 技术树
codex.ts # 叙事数据
prestige.ts # 飞升
db.ts # Prisma
store/
gameStore.ts # Zustand 游戏状态
hooks/
useGameLoop.ts # 主循环
use-toast.ts # 既有
```
## 三、状态模型(Zustand
```ts
interface GameState {
// 资源
crystals: number; // 记忆晶体(主货币)
insights: number; // 技术洞见
fragments: Record<FragmentId, boolean>; // 已获碎片
contact: number; // 接触进度 0-100
// 产能
crystalsPerSec: number; // 由技术树计算
pulsePower: number; // 主动脉冲产出
// 技术
tech: Record<TechId, number>; // 各技术等级
// 飞升
ascensions: number;
blueprints: string[]; // 保留的蓝图
// 解码
currentCrystal: Crystal | null;
// 元
lastTick: number;
prestige: PrestigeBonus;
}
```
## 四、核心循环引擎
- 客户端 `setInterval` 每 100ms tick 一次。
- 每次 tick`crystals += crystalsPerSec * dt`
- 离线收益:加载时计算 `now - lastTick`,按 50% 效率补发(上限 8h)。
- 状态自动持久化到 localStorageZustand persist,节流 1s)。
## 五、解码谜题实现
- 阵列 = 节点图(邻接表),每节点有 `resonance` 值。
- 共振规则:玩家依序点击节点,相邻点击须满足 `resonance` 差值 ≤ 阈值,且最终形成闭合回路。
- 步数限制:规模 3×3=9 节点 / 4×4=16 / 5×5=25,步数上限分别为 6/10/15。
- 成功 → 结算奖励 + 动画;失败 → 晶体不损耗,可重试。
## 六、Canvas 渲染策略
- 单一全屏 Canvas 作为背景层(星空 + 星尘 + 远景星云)。
- 晶体集群与解码阵列用**独立 Canvas 或 SVG**叠加,便于交互。
- requestAnimationFrame 驱动,设备像素比适配,低性能设备降帧。
## 七、可扩展性
- 数值配置集中在 `config.ts`,便于平衡调整与工单需求响应。
- 叙事数据集中在 `codex.ts`,新增碎片只需追加数据。
- 技术树/解码规则/探险地图均数据驱动,支持后续模块化扩展。
+57
View File
@@ -0,0 +1,57 @@
# 解码系统修复说明 (v0.1.1)
## 问题
用户反馈「解码大量的无法通过」。经分析,根因是 `generatePuzzle` **没有保证生成的局面有解**:随机填色后可能根本不存在一条满足「按目标顺序、四邻接、不重复」的路径,导致大量死局。
## 修复方案
### 1. 路径构造法生成器(核心修复)
重写 `generatePuzzle`,采用**路径构造法**
1. 先随机生成目标序列 target。
2. 在网格上**主动构造一条合法路径**:随机起点,每步走向一个未使用的四邻接节点,长度 = target.length。
3. 把路径上第 i 个节点颜色**强制设为 target[i]**。
4. 其余非路径节点随机填色(60% 偏向目标色,提升多解性)。
这样**至少存在一条解**(即构造的路径),玩家不会遇到「怎么都过不了」的死局。
### 2. 精确可解性检测 `isSolvable`
新增 DFS 函数,从当前路径末端出发,精确判断是否存在一条按 target 剩余顺序、四邻接、不重复的完成路径。用于:
- 玩家点击后检测「此路不通」,提示撤销。
- `isStuck` 复用此判定。
### 3. 撤销功能 `undoStep`
新增 store action,支持单步回退(恢复节点 used 状态)。走错时无需整个重排。
### 4. 卡死提示横幅
当局面不可解时,顶部显示「谐振受阻,此路已无解」横幅 + 快捷撤销按钮。
### 5. 可行起点高亮(体验增强)
选起点阶段,用 `canStartFrom` 检测每个目标色节点「选了之后是否仍可解」:
- **可行起点**:强光圈 + 2px 边框 + 快脉冲,引导玩家。
- **死起点**:淡化(opacity 0.5),避免玩家踩坑。
### 6. 旧存档兼容
`init` 时检测存档中的活跃谜题,若不可解(旧算法生成)则自动放回晶体队列,避免老玩家卡死。
## 验证结果
### 算法验证(1000 次/档位)
- Tier 1: 1000/1000 可解 (100.0%)
- Tier 2: 1000/1000 可解 (100.0%)
- Tier 3: 1000/1000 可解 (100.0%)
- 总计: 3000/3000 (100.00%)
### 模拟游玩(带可解性筛选的贪心玩家)
- 500 局: 100% 解出,平均撤销 0 次
### agent-browser 端到端验证
- T1 (3×3): 通过 ✓
- T2 (4×4): 通过 ✓
- 死路检测: 触发「谐振受阻」横幅 ✓
- 撤销: 单步回退正常 ✓
- 可行起点高亮: 准确标记(4 个玫色节点中仅 1 个为可行起点,与手算一致)✓
## 影响文件
- `src/lib/game/decode.ts` — 重写生成器 + 新增 isSolvable/canStartFrom
- `src/store/gameStore.ts` — clickNode 可解性检测 + undoStep + init 兼容
- `src/components/game/DecodeArray.tsx` — 撤销按钮 + 卡死横幅 + 可行起点高亮
+107
View File
@@ -0,0 +1,107 @@
# v0.2 遗迹探险肉鸽系统
> 版本:v0.2.0
> 日期:2025-06-23
> 作者:Super_Z
## 概述
实现路线图中的 v0.2 旗舰功能——**遗迹探险肉鸽系统**。玩家消耗能量派出探险队,进入程序化生成的遗迹节点路径,沿途遭遇战斗、宝藏、抉择、解谜与休整事件,击败终点 BOSS 获取丰厚奖励。
## 新增系统
### 1. 能量系统
- 新增「探险能量」资源,初始 3 点,上限 5 点。
- 每 45 秒自动恢复 1 点能量(tick 驱动,离线也恢复)。
- 每次探险消耗 1 点能量。
### 2. 探险力与生命
- **探险力**:由基础值 10 + 探险分支技术加成 + 飞升蓝图加成 + 飞升周目加成计算。
- **探险生命**:基础 100 + 技术/飞升加成。归零则探险失败结束。
- **战斗胜率**`clamp(power / (power + difficulty × 8), 0.25, 0.95)`
### 3. 节点路径
- 每次探险生成 5-7 个节点的线性路径。
- 末位固定为 BOSS 节点。
- 首节点强制为友好类型(宝藏/休整),保证开局体验。
- 6 种节点类型:
| 类型 | 图标 | 颜色 | 机制 |
|---|---|---|---|
| 战斗 combat | 剑 | 玫红 | 按胜率决定胜负,胜获奖、败扣血或全灭 |
| 宝藏 treasure | 宝石 | 琥珀 | 直接获得晶体+洞见 |
| 抉择 choice | 问号 | 青 | 55% 好结果(奖励)/45% 坏结果(扣血) |
| 解谜 puzzle | 拼图 | 紫 | 探险力越高越可能解出,成功获大奖 |
| 休整 rest | 心 | 翠 | 恢复生命 |
| BOSS boss | 骷髅 | 红 | 必定战斗,高难度,胜则大量奖励+探险胜利 |
### 4. 奖励机制
- **实时入账**:每个节点结算后奖励立即加入玩家资源。
- **晶体绕过仓库上限**:探险奖励晶体不受 idle 产能仓库上限限制(仅闲置产能受限)。
- **失败保留**:探险失败(HP 归零)仍保留已获得的全部奖励。
- **BOSS 胜利**:额外给予大量晶体、洞见、接触进度。
- **撤退**:玩家可随时主动撤退,保留已获奖励。
### 5. 探险日志
- 记录最近 30 条探险事件,含节点类型、结果描述、奖励明细。
- 飞升后保留日志。
## UI 设计
### 探险标签页(主页面第 4 个标签)
- **入口态**(无活跃探险):
- 能量条(含恢复倒计时)
- 出发按钮(消耗 1 能量)
- 探险说明文案
- 最近探险记录(最近 4 条)
- **进行态**(有活跃探险):
- 探险力 + 当前节点位置
- 生命条(HP/maxHP<30% 变红)
- 节点路径图(横向,已通过/当前/未知,含类型图标与颜色)
- 路径进度条
- 当前节点事件卡(类型标签、难度、胜率、标题、描述、结算结果)
- 操作按钮(探索节点 / 前进 / 撤退)
- 本次累计奖励
### 视觉细节
- 每种节点类型有专属颜色与图标
- 当前节点放大 1.25 倍 + 光晕
- 已通过节点显示绿色对勾
- 事件卡使用对应节点类型的渐变背景与边框光晕
- 能量条渐变(琥珀→黄)
- 生命条渐变(玫红→翠绿)
## 配套修复与增强
### 仓库满仓警告
- 晶体达上限 98% 时,底部状态栏变琥珀色 + 脉冲动画 + 警告文案。
- 引导玩家解码或升级仓库。
### 闲置产能上限优化
- 仓库满时不再自动产出晶体(而非产出后截断)。
- 探险奖励晶体可超出上限,不被浪费。
### 技术树探险分支增强
- 远征推进器:探险力 +3
- 遗迹图谱:探险力 +5,探险生命 +20
- 维度信标:探险力 +8,接触进度 +50%
## 验证结果
- ✅ 探险出发/节点结算/前进/BOSS/撤退全流程通过
- ✅ HP clamp 修复(休整不超上限)
- ✅ 探险奖励绕过仓库上限(101 晶体 > 50 cap
- ✅ 探险失败保留奖励
- ✅ 能量恢复计时
- ✅ 探险日志记录
- ✅ 探险标签红点提示(有能量/有进行中探险)
## 影响文件
- `src/lib/game/types.ts` — 新增探险类型定义
- `src/lib/game/expedition.ts`**新建**,探险逻辑(生成/结算/能量恢复)
- `src/lib/game/config.ts` — 初始状态增加探险字段,技术树描述更新
- `src/lib/game/engine.ts` — createInitialState/performPrestige 包含探险字段
- `src/store/gameStore.ts` — 探险 actions + 能量恢复 tick + HP clamp + 产能上限优化
- `src/components/game/ExpeditionPanel.tsx`**新建**,探险 UI
- `src/app/page.tsx` — 新增探险标签页 + 仓库满仓警告 + 能量红点
+161
View File
@@ -0,0 +1,161 @@
# 回响星核 v0.2.1 — 音频系统 + 成就系统 + 视觉打磨
> 版本:v0.2.1
> 日期:2026-06-23
> 范围:程序化音频引擎、14 项成就系统、脉冲粒子爆发、UI 打磨
---
## 一、程序化音频系统(Web Audio API,零资源文件)
### 设计目标
- 无需任何外部音频文件,全部由振荡器 + 噪声 + 包络合成
- 音色契合"深空考古 / 全息晶体"美学:谐振、水晶、低频脉冲
- 受 `soundOn` 开关控制,支持浏览器自动播放策略(首次用户交互后初始化 AudioContext
### 实现
- **文件**`src/lib/game/audio.ts`AudioEngine 单例 + 15 种音效)
- **Hook**`src/hooks/useAudio.ts``useAudioSync` 同步开关,`sfx()` 便捷播放)
### 音效清单
| 音效名 | 触发场景 | 合成方式 |
|--------|---------|---------|
| pulse | 主动脉冲 | 上升正弦短音 + 三角波泛音 |
| pulseCombo | 连击≥3 脉冲 | 更高基频 + 多泛音 |
| decodeClick | 解码点击节点 | 按颜色变调(C5/D5/E5/F5+ 泛音 |
| decodeFail | 解码点击错误 | 低频锯齿 + 噪声脉冲 |
| decodeSuccess | 解码成功 | 大三和弦琶音上扬 |
| fragmentUnlock | 记忆碎片浮现 | 空灵长音 + 扫频 |
| techBuy | 购买技术 | 双音确认 |
| expeditionStart | 探险出发 | 低频锯齿扫频 + 噪声(引擎启动感) |
| expeditionNode | 探险节点结算 | 三角波中频 |
| expeditionVictory | 探险胜利 | 上行凯旋和弦 |
| expeditionDefeat | 探险失败 | 下行低音 |
| prestige | 飞升 | 宏大扫频 + 和弦堆叠 |
| achievement | 成就解锁 | 亮丽四音琶音 |
| uiHover | 界面悬停 | 极轻正弦 |
| uiClick | 界面点击 | 轻确认 |
### 接入点
- `CrystalOrb`pulse / pulseCombo
- `DecodeArray`decodeClick(按颜色变调)/ decodeFail / decodeSuccess / fragmentUnlock
- `ExpeditionPanel`expeditionStart / expeditionNode / expeditionVictory / expeditionDefeat
- `TechTree`techBuy
- `PrestigeDialog`prestige
- `AchievementNotifier`achievement
- `SettingsDialog`:新增「试听」按钮(播放 decodeSuccess
---
## 二、成就系统(14 项,跨周目永久加成)
### 设计目标
- 给玩家长期目标与即时正反馈
- 成就奖励包含即时资源 + 永久产能/洞见百分比加成(跨周目保留,与蓝图叠加)
- 解锁时 Toast 通知 + 专属音效
### 实现
- **定义**`src/lib/game/achievements.ts`14 项成就 + `achievementBonuses` 聚合)
- **状态**`GameState.achievements: Record<string, boolean>`
- **检测**`gameStore.checkAchievements()` 每秒由主循环调用;页面可见性恢复时立即检测
- **通知**`AchievementNotifier` 组件消费 `_achievementQueue`,弹 Toast + 播音效
- **UI**`AchievementsPanel` 组件,作为第 4 个标签页
### 成就列表
| ID | 名称 | 条件 | 奖励 |
|----|------|------|------|
| ach_first_pulse | 初次触碰 | 发起第一次脉冲 | +5 晶体 |
| ach_first_decode | 谐振初鸣 | 解码第一颗晶体 | +3 洞见 |
| ach_decoded_10 | 回响解码者 | 累计解码 10 颗 | +15 洞见 · 产能 +5% |
| ach_decoded_25 | 记忆织匠 | 累计解码 25 颗 | +40 洞见 · 产能 +8% |
| ach_decoded_50 | 星核解密师 | 累计解码 50 颗 | +100 洞见 · 产能 +12% · 洞见 +10% |
| ach_tech_3 | 初窥门径 | 解锁 3 项技术 | +20 洞见 |
| ach_tech_all | 全谱精通 | 解锁全部 12 项技术 | 产能 +15% · 洞见 +15% |
| ach_frag_4 | 残篇拾遗 | 拼凑 4 段碎片 | +10 接触 · +25 洞见 |
| ach_frag_all | 回响全谱 | 拼凑全部 8 段碎片 | +25 接触 · 产能 +10% |
| ach_exp_1 | 初探遗迹 | 完成 1 次探险 | +10 洞见 |
| ach_exp_5 | 遗迹猎手 | 出发 5 次探险 | +30 洞见 · 产能 +5% |
| ach_prestige_1 | 初次接触 | 飞升 1 次 | 产能 +10% · 洞见 +10% |
| ach_prestige_3 | 维度行者 | 飞升 3 次 | 产能 +20% · 洞见 +20% |
| ach_warehouse | 满仓时刻 | 晶体达仓库上限 | +8 洞见 |
### 加成计算
`recomputeStats()` 现聚合三层数值来源:
1. 技术树节点效果
2. 飞升蓝图加成(每张 +5% 产能 / +3% 洞见 / +4% 接触)
3. **成就永久加成**(跨周目保留,飞升后仍生效)
### 旧存档兼容
- `init()` 补全缺失的 `achievements` 字段为 `{}`
- `partialize` 排除 `_achievementQueue`(运行时临时队列不持久化)
---
## 三、视觉打磨
### CrystalOrb 增强
- **粒子爆发**:每次脉冲生成 6–12 个粒子,沿径向发散(连击越高粒子越多)
- **脉冲扩散环**:点击时从晶体向外扩散的绿色光环
- **浮动数字变色**:连击≥3 紫色、≥5 琥珀色,带对应颜色光晕
- 新增 `echo-ring` / `echo-burst` 关键帧动画
### 标签栏
- 从 4 列扩展为 5 列(新增「成就」)
- 成就标签有未解锁时显示琥珀色脉冲点
- 标签字号微调以适配 5 列
### StatsPanel
- 新增「已解锁成就」「累计探险」「探险能量」三行
---
## 四、QA 验证
### agent-browser 自动化测试
- ✅ 页面加载 HTTP 200,无运行时错误
- ✅ 解码谜题:生成 → 求解(按目标色序点击相邻同色节点)→ 奖励入账(绕过仓库上限)
- ✅ 探险系统:出发 → 探索节点 → 获得奖励 → 前进 → 日志记录
- ✅ 成就面板:14 项渲染,3 项已解锁(初次触碰/谐振初鸣/满仓时刻),永久加成区域正确显示
- ✅ 统计面板:成就计数 3/14,探险次数,能量条
- ✅ 设置面板:音效试听按钮可点击,开关可用
### VLM 视觉验证
- 成就面板:深色背景 + 科技光效 + 锁/解锁状态分明,色彩对比鲜明
- 晶体集群:渐变晶核 + 旋转环 + 粒子爆发正常
### Lint
- `bun run lint` 零错误
---
## 五、文件变更清单
**新增**
- `src/lib/game/audio.ts` — 程序化音频引擎
- `src/hooks/useAudio.ts` — 音频 React 适配
- `src/lib/game/achievements.ts` — 成就定义与聚合
- `src/components/game/AchievementsPanel.tsx` — 成就面板
- `src/components/game/AchievementNotifier.tsx` — 成就 Toast 消费者
**修改**
- `src/lib/game/types.ts` — GameState 新增 `achievements` 字段
- `src/lib/game/config.ts` — INITIAL_STATE 新增 `achievements: {}`
- `src/lib/game/engine.ts` — recomputeStats 聚合成就加成;performPrestige/createInitialState 保留成就
- `src/store/gameStore.ts` — checkAchievements / consumeAchievementQueueinit/hardReset/partialize 兼容
- `src/hooks/useGameLoop.ts` — 每秒检测成就 + 可见性恢复检测
- `src/app/page.tsx` — 5 列标签 + 成就页 + AchievementNotifier + useAudioSync
- `src/components/game/CrystalOrb.tsx` — 粒子爆发 + 扩散环 + 浮动数字变色 + pulse 音效
- `src/components/game/DecodeArray.tsx` — decodeClick/fail/success/fragment 音效
- `src/components/game/ExpeditionPanel.tsx` — start/node/victory/defeat 音效
- `src/components/game/TechTree.tsx` — techBuy 音效
- `src/components/game/PrestigeDialog.tsx` — prestige 音效
- `src/components/game/SettingsDialog.tsx` — 音效试听按钮 + 文案更新
---
## 六、下一步规划
- **v0.3 剩余**:socket 全局「星潮」实时事件(需 mini-service
- **v0.4**:云存档 + 排行榜
- **v0.5**:全 5 纪元叙事扩展
- 持续关注 Gitea 工单
+113
View File
@@ -0,0 +1,113 @@
# 回响星核 v0.3 — 星潮事件系统(周期性全局玩法修饰)
> 版本:v0.3
> 日期:2026-06-23
> 范围:6 种星潮事件、周期触发、全屏视觉叠层、程序化音效、寂静期补偿机制
---
## 一、设计动机
回应 Gitea Issue #1「就一直连连看?」的玩法单一反馈。星潮事件为放置循环注入**动态变化**:深空中偶发的「星潮」会短暂改变物理法则,让玩家在 75 秒内体验产能翻倍、解码加成、探险增强等不同玩法侧重,打破单调感。
## 二、6 种星潮事件
| 类型 | 名称 | 效果 | 色调 | 性质 |
|------|------|------|------|------|
| crystal | 晶体潮 | 采矿产能 ×2 | 翠绿 | 正面 |
| resonance | 谐振风暴 | 解码奖励 ×1.6 | 玫红 | 正面 |
| ruins | 遗迹共振 | 探险力 +5、生命 +30 | 琥珀 | 正面 |
| void | 虚空低语 | 洞见获取 ×2 | 紫红 | 正面 |
| core | 星核悸动 | 接触率 ×3、脉冲威力 ×2 | 青色 | 正面 |
| silence | 寂静期 | 产能 ×0.5;结束时按时长补偿洞见 | 灰蓝 | 负面(有补偿) |
## 三、触发机制
- **首次星潮**:游戏开始 30 秒后触发(`TIDE_CONFIG.firstDelay`
- **常规间隙**:星潮结束后 40 秒触发下一次(`TIDE_CONFIG.gap`
- **持续时间**75 秒(`TIDE_CONFIG.duration`
- **权重抽取**:6 种事件按权重随机(正面事件权重高,寂静期权重最低 10%)
- **飞升重置**:飞升后清空当前星潮,重置计时,30 秒后重新开始
## 四、修饰器应用层
| 修饰器 | 应用点 |
|--------|--------|
| crystalsPerSecMult | `tick()` 即时乘(显示基础值,实际翻倍积累) |
| pulsePowerMult | `pulse()` 即时乘 |
| decodeRewardMult | `clickNode()` / `autoDecodeTick()` 结算时乘 |
| insightMultAdd | 进 `recomputeStats` 缓存(星潮起止时重算) |
| contactRateMult | 进 `recomputeStats` 缓存 |
| expeditionPowerBonus / hpBonus | `startExpedition()` 即时加 |
| silenceInsightPerSec | 星潮结束时 `computeSilenceCompensation` 结算 |
## 五、UI 表现
### 顶部指示器(`StarTideIndicator`
- 活跃时在 header 右侧显示彩色芯片:图标 + 名称 + 倒计时秒数
- 芯片背景为对应色半透明,带光晕,进度条随时间推进
### 全屏背景叠层(`StarTideOverlay`
- 顶部 + 底部对应色渐变色带
- 边缘 `inset box-shadow` 呼吸光效(3 秒周期)
- 活跃时整页笼罩对应色调氛围
### 通知(`StarTideNotifier`
- 星潮降临时 Toast + `tideStart` 音效(神秘扫频)
- 星潮结束时 Toast + `tideEnd` 音效(柔和消退)
- 寂静期结束时显示「虚空回赠 N 洞见」+ `techBuy` 音效
### Footer 联动
- 活跃时 footer 提示文字变为「✺ 星潮「晶体潮」进行中 · 深空晶体涌动…」
- footer 边框点亮对应色光晕
## 六、程序化音效
新增 2 种音效(`audio.ts`):
- `tideStart`330→660Hz 正弦扫频 + 440→880Hz 三角波 + 523/784Hz 长音(神秘降临感)
- `tideEnd`659→523→392Hz 下行三音(柔和消退)
## 七、状态与兼容
- `GameState` 新增 `activeTide: StarTide | null``lastTideEnd: number`
- 旧存档兼容:`init()` 补全字段为 `null/0`
- `partialize` 排除运行时队列 `_tideEvents`
- `performPrestige` 重置星潮(清空 activeTidelastTideEnd 设为 now
## 八、QA 验证
### agent-browser 自动化
- ✅ 强制触发星潮:`lastTideEnd=0` → 重载 → 30s 内自动触发,状态显示 `activeTide: crystal`
- ✅ 顶部指示器渲染:「✺ 晶体潮 64s」绿色芯片
- ✅ 背景叠层:VLM 确认绿色调背景光晕 + 顶部/底部色带 + 边缘呼吸光
- ✅ 寂静期补偿:设置 `type=silence, endsAt=now+3s` → 等待结束 → insights 从 50 增至 81(补偿 30
- ✅ Footer 联动:活跃时显示「✺ 星潮「晶体潮」进行中 · 深空晶体涌动…」
- ✅ 版本号 v0.3 显示
- ✅ lint 零错误,HTTP 200,无运行时错误
### VLM 视觉确认
- 指示器芯片:绿色文字「晶体潮 11s」
- 背景光晕:深绿色,顶部/底部色带 + 边缘呼吸光
- 视觉完成度:界面清晰、风格统一、细节良好
## 九、文件变更清单
**新增**
- `src/lib/game/starTide.ts` — 星潮定义、修饰器、触发逻辑
- `src/components/game/StarTide.tsx` — 指示器 + 叠层 + 通知三组件
**修改**
- `src/lib/game/types.ts` — GameState 新增 `activeTide` / `lastTideEnd`
- `src/lib/game/config.ts` — INITIAL_STATE 新增潮汐字段
- `src/lib/game/engine.ts``recomputeStats` 应用 insightMultAdd/contactRateMult`performPrestige`/`createInitialState` 处理潮汐
- `src/lib/game/audio.ts` — 新增 `tideStart` / `tideEnd` 音效
- `src/store/gameStore.ts``tickTide` / `consumeTideEvents`tick/pulse/clickNode/autoDecodeTick/startExpedition 应用修饰器;init/hardReset/partialize 兼容
- `src/hooks/useGameLoop.ts` — 主循环调用 `tickTide`,可见性恢复补检测
- `src/app/page.tsx` — 接入 StarTideOverlay/Indicator/NotifierFooter 联动;版本号 v0.3
## 十、下一步规划
- v0.3 已完成客户端星潮(原计划 socket 全局事件,现以单机版先行,后续可扩展为多人同步)
- v0.4:云存档 + 排行榜
- v0.5:全 5 纪元叙事扩展
- 持续关注 Gitea 工单
+171
View File
@@ -0,0 +1,171 @@
# v0.3.1 · 星图天文台(Constellation Observatory)元进程天赋系统
> 第二次回应 Issue #1「就一直连连看?」——为飞升循环加入 Slay the Spire 式 3 选 1 天赋 draft
> 跨周目永久生效,配合 Canvas 动态星图可视化,提供长期成长目标与策略选择。
## 一、设计动机
v0.3 星潮事件已为放置循环注入动态变化,但玩家仍缺长期 meta-progression 目标。本系统在飞升后触发:
- **策略选择**:3 选 1 的 draft 让每次飞升都有差异化构建方向
- **永久成长**:天赋跨周目保留,配合蓝图/成就形成三层加成
- **视觉反馈**:Canvas 动态星图,每解锁一个天赋就在对应星座点亮一颗星
- **目标驱动**:18 个天赋 + 2 个专属成就,给玩家清晰的中长期目标
## 二、系统架构
### 2.1 数据层(`src/lib/game/constellation.ts`
```typescript
// 6 大星座 × 3 颗星 = 18 个天赋
type ConstellationCategory =
| "mining" | "decoding" | "expedition"
| "contact" | "economy" | "cosmic";
interface ConstellationPerk {
id: string; // c_min_1, c_dec_2, ...
category: ConstellationCategory;
name: string;
desc: string;
order: 1 | 2 | 3; // 星座内位置
}
```
### 2.2 18 个天赋一览
| 类别 | #1 | #2 | #3 |
|------|-----|-----|-----|
| 翠·永动矿脉 | 永动钻头(产能+20%) | 深井网络(上限+50%) | 谐振熔炉(脉冲+50%) |
| 玫·光谱矩阵 | 光谱记忆(洞见+15%) | 步幅延展(步数+1) | 自动校准(自解-3s) |
| 琥·远征星图 | 维生护盾(生命+25%) | 信标矩阵(探险力+20%) | 能量共振(能量上限+1) |
| 紫·接触回响 | 接触共鸣(接触率+20%) | 飞升加速(门槛-10) | 蓝图回响(每蓝图+2%全属性) |
| 灰·虚空市场 | 离线缓存(离线+15%) | 晶体富集(T2/T3+8% | 星潮引导(间隙-10s |
| 金·宇宙回响 | 飞升礼包(+30晶体/+5洞见) | 二周目经验(解码+10%) | 全息共振(全产能+8%) |
### 2.3 修饰器聚合
`constellationBonuses(perks: string[]): ConstellationModifiers` 返回 **19 项修饰器**
`recomputeStats()` / `tickTide()` / `tick()` / `clickNode()` / `autoDecodeTick()` / `computeExpeditionPower()` / `computeExpeditionHp()` 等所有相关用点处应用。
### 2.4 状态扩展
```typescript
// types.ts
interface GameState {
// ... 既有字段
constellation: string[]; // 已解锁天赋 ID
pendingPerkChoices: string[] | null; // 飞升后待选 3 个
}
// config.ts INITIAL_STATE
constellation: [],
pendingPerkChoices: null,
```
### 2.5 飞升流程
```
performPrestige(state)
├─ 计算 newBp(蓝图数)
├─ rollPerkChoices(constellation) → 3 个不同类别的可选天赋
├─ 应用「飞升礼包」补偿(+30 晶体 / +5 洞见,若已解锁)
├─ 重置资源/技术/接触进度
└─ 保留:fragments / totalDecoded / blueprints / achievements / constellation
```
飞升后 `pendingPerkChoices` 不为空 → `ConstellationDialog` 自动弹出 → 玩家选 1 → `chooseConstellationPerk(perkId)` 写入 constellation 并清空 pending。
## 三、UI 组件
### 3.1 ConstellationPanel(星图标签内容)
**Canvas 动态渲染**requestAnimationFrame 60fps):
- 六边形布局 6 个星座中心点
- 每星座内 3 颗星(三角形排布:order 1 顶部,2/3 底部)
- **未解锁星**:暗灰小点 + 弱光晕
- **已解锁星**:类别色填充 + 强光晕 + 4 道十字光线 + 闪烁动画
- **同星座已解锁星之间**:连接线(类别色 60% 透明度)
- **星座 → 中心星核**:虚线(已解锁 ≥1 时显示,呼吸效果)
- **中心星核**:紫红色径向渐变,半径随时间正弦呼吸
- **Hover**:星点 14px 范围内显示 tooltip(天赋名/描述/类别/序号)
**底部信息区**
- 6 类别图例条(彩色圆点 + 名称 + 已解锁/总数)
- 已解锁天赋列表(彩色边框 + 名称 + 描述 + 类别标签)
- 空状态:虚线边框卡片 + 引导文案
### 3.2 ConstellationDialog(飞升后 draft 模态)
- 自动 open`pendingPerkChoices` 非空时
- 3 张卡片:左侧类别色条 + 圆形首字图标 + 天赋名 + 类别徽章 + 描述
- 选中动画:8 个粒子 ping + 卡片缩放 + 类别色发光
- 「重新抽取」按钮:rerollPerkChoices(依然保证 3 不同类别)
- 底部说明:「选择后永久保留,跨周目生效」
### 3.3 顶部「觉醒」按钮
`pendingPerkChoices` 存在时显示,fuchsia 边框 + animate-pulse,点击打开 ConstellationDialog。
## 四、平衡设计
### 4.1 单天赋强度
- 每个天赋加成约 +15%~+25% 单项,避免数值膨胀
- 「飞升礼包」给前期起飞资源(30 晶体 + 5 洞见)
- 「全息共振」给全局 +8%,作为终极通用天赋
### 4.2 跨周目叠加
- 18 个天赋全解锁 ≈ 总加成 200%~300%(与 6 蓝图、14 成就叠加后约 5x~8x)
- 配合飞升次数加成(每次 +15% 探险力、+10 生命),后期探险胜率显著提升
### 4.3 类别多样性
- `rollPerkChoices` 第一轮强制不同类别,避免 3 选全是同类
- 玩家可在 6 类别间自由构建:采矿流/解码流/探险流/速飞升流/经济流/全能流
## 五、QA 验证记录
| 测试项 | 结果 |
|--------|------|
| 星图标签页渲染 | ✅ 6 类别图例 + Canvas + 空状态 |
| 对话框自动弹出 | ✅ pendingPerkChoices 设置后立即开 |
| 3 卡片显示 | ✅ 来自 3 不同类别 |
| 点击天赋 | ✅ constellation 添加,crystalsPerSec 提升,pending 清空 |
| 重新抽取 | ✅ 3 张新卡片(依然不同类别) |
| 多天赋叠加 | ✅ c_min_1+c_con_1 → 产能 1.416 / 接触率 1.2 |
| 6 类别成就触发 | ✅ 六分星辉 Toast 弹出 |
| VLM 视觉确认 | ✅ 单星点亮(绿色永动矿脉) |
| lint | ✅ 零错误 |
| 编译 | ✅ < 200ms |
## 六、与既有系统的协同
```
飞升(Prestige)
┌─────────┼─────────┐
▼ ▼ ▼
蓝图+ 星图天赋+ 成就+
(5%/3%/4%) (各天赋) (各成就)
│ │ │
└─────────┴─────────┘
recomputeStats()
┌─────────┼─────────┐
▼ ▼ ▼
产能 解码奖励 探险力/生命
星潮修饰(即时乘)
实际游戏体验
```
## 七、后续扩展方向
- **天赋觉醒动画**:选择天赋时全屏闪光 + 星图缩放
- **天赋重置**:消耗稀有资源重置 constellation,重新选择
- **隐藏天赋**:达成特殊条件解锁(如「飞升 10 次自选 1 天赋」)
- **天赋树依赖**order 2 需要 order 1 解锁(增加构建深度)
- **socket 同步**:多人共享同一星图,协作解锁
+175
View File
@@ -0,0 +1,175 @@
# v0.4 回响编年史 · Chronicle of Echoes
> 跨周目叙事时间轴系统。每次飞升自动铭刻一条纪元条目,记录本周目的解码、探险、星潮、天赋觉醒与里程碑,形成永恒叙事流。朝 v0.5「全 5 纪元叙事」目标推进。
## 动机
- **回应 Issue #1**「就一直连连看?」的进一步反馈:星潮+星图解决了「玩法单一」,但仍缺**叙事深度**与**进度回顾**。
- 玩家完成多次飞升后无回顾入口,对「自己走过多远」缺乏感知,影响长线留存。
- v0.5 路线图为「全 5 纪元叙事」,编年史是其底层骨架——先把数据结构与展示框架立起来。
## 核心模块
### 数据模型(`src/lib/game/types.ts`
```ts
export interface ChronicleEntry {
ascensionNumber: number; // 第几次飞升(1-based
epochName: string; // 程序化生成的纪元名(5 纪元循环)
timestamp: number;
durationSec: number; // 本周目持续时间
summary: {
techsUnlocked: number;
constellationsTotal: number;
expeditionsCompleted: number;
bossKills: number;
starTidesEncountered: number;
blueprintsAfter: number;
crystalsDecoded: number;
crystalsDecodedThisRun: number;
};
perksThisAscension: string[]; // 本次飞升后选择的天赋
tidesThisRun: string[]; // 本周目新遇到的星潮 ID
lore: string; // 程序化生成的叙事文本
milestones: string[]; // 里程碑标签
}
export interface RunStartSnapshot {
timestamp: number;
techsUnlocked: number;
expeditionsCompleted: number;
bossKills: number;
starTidesEncountered: string[];
crystalsDecoded: number;
}
```
GameState 新增字段:
- `chronicle: ChronicleEntry[]` — 编年史条目列表(上限 50
- `runStart: RunStartSnapshot` — 当前周目起始快照(用于 delta 计算)
- `bossKills: number` — 累计 BOSS 击破数(跨周目)
- `starTidesEncountered: string[]` — 累计遇到的星潮 ID 集合(去重)
### 编年史逻辑(`src/lib/game/chronicle.ts`,新建)
- `EPOCH_NAMES` — 5 纪元 × 4 后缀 = 20 种纪元名组合(如「第一纪元 · 觉醒之晨」「第三纪元 · 虚空回望」)
- `generateEpochName(ascensionNumber, seed)` — 按 ascensionNumber 循环 5 纪元,seed 决定后缀
- `buildLore(entry)` — 模板化叙事生成:开场 → 核心活动 → 星潮段落 → 觉醒段落 → 里程碑 → 收尾
- `detectMilestones(state, deltas)` — 9 种里程碑检测(首次飞升/首杀 BOSS/单周目解码 10+/技术狂人/星潮亲历者/星图六分等)
- `computeRunDeltas(state)` — 基于 runStart 快照计算本周目 delta(持续时间/解码数/技术数/探险数/BOSS/新星潮)
- `buildChronicleEntry(state, perks)` — 飞升时构建一条 ChronicleEntry
- `createRunStartSnapshot(state)` — 飞升后创建新 runStart
- `migrateChronicleFields(state)` — 旧存档兼容:补全 chronicle=[]/runStart/bossKills=0/starTidesEncountered=[]
- `withPerks(entry, perks)` — 飞升后选天赋时回填 perksThisAscension,并重新生成 lore 包含天赋名
- `getPerkCategoryBreakdown(perks)` / `getCategoryName` / `getCategoryColor` — UI 辅助
### 引擎接入(`src/lib/game/engine.ts`
- `performPrestige` 在重置前:
1. `buildChronicleEntry(state, [])` 构建本周目条目(perks 暂为空,待玩家选择后回填)
2. 追加到 `chronicle` 数组(上限 50 条)
3. `createRunStartSnapshot(state)` 创建新周目的快照
- 保留跨周目字段:`chronicle`, `bossKills`, `starTidesEncountered`
- 重置 `runStart` 为新快照
### Store 接入(`src/store/gameStore.ts`
- `init()` — 调用 `migrateChronicleFields` 补全旧存档
- `tickTide` — 新星潮触发时,将 `tide_${type}` 追加到 `starTidesEncountered`(去重)
- `resolveCurrentNode` — BOSS 战胜利结算时,`bossKills++`
- `chooseConstellationPerk` — 选择天赋后,调用 `withPerks` 回填最近一条 chronicle entry 的 `perksThisAscension`,并重新生成 lore
### UI 组件(`src/components/game/ChronicleDialog.tsx`,新建)
- 顶部 BookOpen 图标按钮(带 badge 显示条目数)
- 全屏对话框,深紫黑渐变背景 + 顶部三色装饰条
- 顶部总览统计:飞升次数 / BOSS 击破 / 星潮亲历
- 垂直时间轴:
- 左侧渐变时间轴线
- 每条卡片左侧有发光圆形节点(5 纪元循环色:翠/玫/琥/紫/金),最新一条带 ping 动画
- 卡片含:#编号 / 纪元名(带 textShadow 光晕)/ 时间戳+持续时长 / 叙事文本(衬线字体)/ 6 项统计芯片 / 星潮徽章列表 / 天赋徽章 / 里程碑徽章
- 入场动画:fade-in + slide-up,错峰 80ms
- 空状态:根据 ascensions 是否 > 0 显示不同提示
- 打开时播放 `chronicleOpen` 翻页音效
### 音频(`src/lib/game/audio.ts`
- `chronicle` — 编年史铭刻音效:G3 基音 + C4/G4/C5/G5 多层泛音,深沉钟声感
- `chronicleOpen` — 翻页音效:A4→D5→A5 轻柔上升 + 短促气流
- 飞升成功时延迟 600ms 触发 `chronicle` sfx,与飞升音分层
### 成就(`src/lib/game/achievements.ts`
新增 6 项成就:
- `ach_chronicle_1` 「首部编年」— 完成首次飞升(+40 洞见 · 产能 +6%)
- `ach_chronicle_3` 「三纪元回响」— 完成 3 次飞升(产能 +10% · 洞见 +10%)
- `ach_chronicle_5` 「五纪元闭环」— 完成 5 次飞升(产能 +15% · 洞见 +15%)
- `ach_boss_1` 「首杀维度」— 击破首个 BOSS(+25 洞见 · 产能 +4%
- `ach_boss_5` 「维度猎手」— 累计击破 5 个 BOSS(产能 +10% · 洞见 +8%
- `ach_tides_all` 「星潮亲历者」— 经历全部 6 种星潮(产能 +8% · 洞见 +8%)
### 飞升对话框增强(`src/components/game/PrestigeDialog.tsx`
- 新增「编年史铭刻预告」卡片,提示本次飞升将记录入编年史
- **修复关键 BUG**:原 `disabled={newBp <= 0 || confirming}``confirming=true` 时按钮永久禁用,玩家无法完成飞升!现已改为 `disabled={confirming && !canReconfirm}` 并加 900ms 冷却窗口
- **修复 UX**:当 newBp=0(无可获蓝图)时不再永久禁用飞升按钮——v0.3.1 星图天赋 + v0.4 编年史已为飞升提供足够动机。按钮文字改为「飞升(无新蓝图)」
- 飞升成功 toast 增加「编年史已铭刻新纪元」提示
### 统计面板增强(`src/app/page.tsx`
新增 3 行统计:
- BOSS 击破次数
- 星潮亲历数(X/6
- 编年史条目数(X 纪元)
## QA 验证
### 修复的关键 BUG
- 飞升按钮 `disabled` 逻辑错误导致玩家永远无法飞升(`confirming` 变 true 后按钮被禁用)
- 验证:agent-browser 端到端测试,第一次点飞升 → 900ms 冷却 → 第二次点击成功触发,星图觉醒对话框弹出
### 编年史端到端测试
1. 设置 contact=100 + 资源,触发飞升 → ascensions 1→2, chronicle 0→1 ✓
2. 选择天赋「二周目经验」→ constellation 增加 c_cos_2, chronicle 末条 perksThisAscension=["c_cos_2"] ✓
3. 打开编年史对话框 → 显示 1 条卡片,含完整 lore、统计芯片、星潮徽章、天赋徽章、里程碑 ✓
4. 再次飞升 → ascensions 2→3, chronicle 1→2 ✓
5. 第二条卡片正确显示新纪元名「第三纪元 · 虚空回望」+ 正确的星潮名「星核悸动」+ 正确的 duration delta2分42秒) ✓
6. 成就解锁:`ach_chronicle_1`「首部编年」+ `ach_prestige_3`自动检测 ✓
7. 统计面板新增「BOSS 击破」「星潮亲历」「编年史条目」3 行 ✓
8. VLM 视觉评估:8/10,无瑕疵,时间轴清晰,纪元颜色区分明显
### 视觉细节
- 5 纪元循环色:翠 #34d399 / 玫 #fb7185 / 琥 #fbbf24 / 紫 #e879f9 / 金 #fcd34d
- 时间轴节点带 boxShadow 光晕(12px + 24px 双层)
- 最新条目带 animate-ping 脉冲
- 卡片入场动画:fade-in + slide-in-from-bottom-2,错峰 80ms
- 叙事文本使用 font-serif 衬线字体增加文学感
- 统计芯片按类别用对应颜色 tint
### 旧存档兼容
- `migrateChronicleFields` 自动补全 chronicle=[]/runStart/bossKills=0/starTidesEncountered=[]
- 玩家旧存档不丢失进度,新字段以默认值初始化
## 文件清单
新增:
- `src/lib/game/chronicle.ts` — 编年史核心逻辑
- `src/components/game/ChronicleDialog.tsx` — 编年史对话框 UI
- `docs/repo/docs/09-回响编年史系统-v0.4.md` — 本文档
修改:
- `src/lib/game/types.ts` — 新增 ChronicleEntry / RunStartSnapshot 类型 + GameState 字段
- `src/lib/game/config.ts` — INITIAL_STATE 新增 chronicle/runStart/bossKills/starTidesEncountered
- `src/lib/game/engine.ts` — performPrestige 构建编年史条目 + 重置 runStart
- `src/lib/game/achievements.ts` — 新增 6 项成就
- `src/lib/game/audio.ts` — 新增 chronicle / chronicleOpen 音效
- `src/store/gameStore.ts` — init 兼容 + tickTide 记录 + resolveCurrentNode BOSS 计数 + chooseConstellationPerk 回填
- `src/components/game/PrestigeDialog.tsx` — 修复 BUG + 编年史预告卡片 + 音效
- `src/app/page.tsx` — 头部编年史按钮 + 对话框渲染 + 统计面板新行 + 版本号 v0.4
## 已知限制 / 后续
- 编年史上限 50 条(超过自动丢弃最早的)
- v0.4 之前发生的飞升不会被回填(无数据可恢复)—— 空状态有对应提示
- 5 纪元叙事内容仍为模板化生成,v0.5 将加入手写剧情节点
- 编年史目前纯本地,v0.4 云存档+排行榜会将其纳入同步范围
+151
View File
@@ -0,0 +1,151 @@
# 深空信标系统 · v0.5(每日挑战 + 本地排行榜)
> 版本:v0.5 · 日期:2026-06-23
> 路线图对应:v0.5 排行榜(本地版先行)+ 每日挑战动机注入
## 动机
v0.4 编年史系统完成后,游戏已有 6 大玩法层(采矿/解码/技术树/图谱/飞升 + 探险 + 星图 + 星潮 + 编年史),但缺少"今日目标"型的短期动机。玩家进入游戏后没有明确的"今天该做什么"指引。
本版本新增**深空信标(Deep Space Beacon**系统:
- **每日挑战**:基于 UTC 日期种子的确定性挑战,全球同一天同一挑战,每天 0 点(UTC)刷新
- **本地排行榜**:记录历次挑战得分,Top 20,纯 localStorage 持久化,无需后端
- 给放置循环注入"今日目标",并为 v0.5 路线图的"云排行榜"奠定数据结构基础
## 核心设计
### 每日挑战(Daily Challenge
- **确定性生成**`generateDailyChallenge(now)` 基于 UTC 日期 keyYYYY-MM-DD)派生 FNV-1a 种子 → mulberry32 PRNG,保证同一天全球玩家看到同一挑战
- **5 种挑战类型**
| 类型 | 标签 | 目标范围 | 单位 |
|------|------|----------|------|
| `decode` | 解码协议 | 6-15 × 难度倍率 | 颗 |
| `expedition` | 远征指令 | 1-3 × 难度倍率 | 次 |
| `pulse` | 脉冲任务 | 20-49 × 难度倍率 | 次 |
| `boss` | 猎杀契约 | 1 × 难度倍率 | 处 |
| `insight` | 洞见采集 | 40-119 × 难度倍率 | 点 |
- **3 档难度**(加权抽取):
| 难度 | 标签 | 倍率 | 权重 | 颜色 |
|------|------|------|------|------|
| `routine` | 常规信标 | ×1.0 | 55% | 翠 |
| `anomaly` | 异常波动 | ×1.6 | 33% | 琥 |
| `singular` | 奇点回响 | ×2.4 | 12% | 玫 |
- **奖励**:完成时按类型 + 难度发放洞见 / 接触进度
- **倒计时**:显示距 UTC 次日 0 点的 HH:MM:SS 倒计时
### 得分计算
```
得分 = 完成度 × 1000 × 难度倍率 + 速度奖励
速度奖励 = max(0, 500 - 完成时长秒 × 0.5) // 仅完成时计算
```
例:常规难度 × 1.090 秒完成 → 1000 + (500 - 45) = 1455 分
### 进度追踪(独立 localStorage
- 进度独立存储于 `echo-nexus-beacon-prog-v1`**不污染 GameState**,避免触发存档迁移
- 在 gameStore 关键动作后调用 `trackBeacon(type, delta)`
- `pulse`:每次脉冲 +1
- `decode`:每次解码完成 +1(手动 + 自动解码均计)
- `expedition`:每次探险结束 +1(胜负都计)
- `boss`:每次 BOSS 胜利 +1
- `insight`:每次洞见获取按数值累加
- 挑战类型与今日挑战不匹配时 `trackBeacon` 直接 return,零开销
### 本地排行榜
- 存储于 `echo-nexus-beacon-lb-v1`Top 20,按得分降序
- 每条记录含:时间戳 / 日期 key / 挑战类型 / 难度 / 完成度 / 得分 / 用时
- UI:奖牌图标(1st 金 / 2nd 银 / 3rd 铜)+ 难度色点 + 类型名 + 用时 + 得分
- 今日记录高亮(fuchsia 边框)
## 文件变更
### 新增
- `src/lib/game/beacon.ts`~290 行):核心逻辑模块
- `generateDailyChallenge()` — 确定性挑战生成
- `computeBeaconScore()` — 得分计算
- `loadLeaderboard()` / `pushLeaderboardEntry()` — 排行榜 CRUD
- `loadDailyProgress()` / `saveDailyProgress()` / `addBeaconProgress()` — 进度 CRUD
- `claimBeaconReward()` — 领取奖励 + 推送排行榜
- `msUntilNextDay()` / `formatCountdown()` — 倒计时
- `src/components/game/BeaconPanel.tsx`~280 行):UI 面板
- 每日挑战卡片(难度色 + 脉冲环动画 + 进度条 + 领取按钮)
- 本地排行榜(Top 20 + 奖牌 + 今日高亮)
- 难度图例
- 倒计时
### 修改
- `src/store/gameStore.ts`
- 新增 `trackBeacon()` 模块级辅助函数
- 新增 `grantBeaconReward(insights, contact)` action
- 在 `pulse` / `clickNode`(完成) / `autoDecodeTick` / `resolveCurrentNode` 后接入 `trackBeacon`
- `src/app/page.tsx`
- 新增第 7 个标签页「信标」(grid-cols-7
- 新增 `beaconClaimable` 状态(2s 轮询,可领取时显示绿点)
- StatsPanel 新增「信标最高分」行
- 版本号 v0.4 → v0.5
- `src/lib/game/chronicle.ts`
- 导出 `buildLore()`(原私有)
- 新增 `regenerateLoreFromEntry(entry)` — 显示时重新生成 lore,修复历史条目中 `tide_ruins` 等原始键名
- `src/components/game/ChronicleDialog.tsx`:叙事文本改用 `regenerateLoreFromEntry(entry)` 替代 `entry.lore`
## BUG 修复
### 编年史历史条目显示原始 tide 键名
- **现象**:v0.4 之前创建的编年史条目 lore 中显示 `tide_ruins` 而非「遗迹共振」(最新条目已修复,旧条目未回填)
- **根因**:lore 文本在条目创建时一次性生成并存储,历史条目存储了修复前的错误文本
- **修复**:新增 `regenerateLoreFromEntry()`,在 ChronicleDialog 显示时从 entry 的结构化数据(tidesThisRun / perksThisAscension / milestones)重新生成 lore,保证历史与未来条目命名一致
- **验证**:第二纪元 lore 现正确显示「遗迹共振、晶体潮、虚空低语、谐振风暴」
## QA 验证(agent-browser + VLM
### 编年史 BUG 修复验证
- ✅ 第二纪元 lore 显示「遗迹共振、晶体潮、虚空低语、谐振风暴」(修复前显示 `tide_ruins` 等原始键)
- ✅ 第三纪元 lore 保持正确「星核悸动」
- ✅ 徽章显示正确
### 深空信标系统验证
- ✅ 第 7 个标签页「信标」渲染正确
- ✅ 每日挑战卡片:难度标签 + 类型标签 + 标题 + 描述 + 进度条 + 奖励 + 领取按钮
- ✅ 倒计时显示「08:47:12」并每秒更新
- ✅ 难度图例显示 3 档(×1 / ×1.6 / ×2.4
- ✅ 进度追踪:解码 1 颗晶体 → 洞见采集进度 +12(解码奖励的洞见值)
- ✅ 完成挑战后按钮变为「领取奖励」
- ✅ 领取奖励后:contact +5.113.47 → 18.57),按钮变「已领取」
- ✅ 排行榜生成条目:「洞见采集 ✓ 1m30s 1.46K」
- ✅ 统计面板新增「信标最高分」行
- ✅ 可领取时标签页显示绿点提示
- ✅ VLM 视觉评估:高美观度,颜色搭配佳,无重叠 bug
### 全系统回归验证
- ✅ 主视图(资源条 / 晶体球 / 解码面板)正常
- ✅ 探险全流程(出发 → 探索 → 前进 → BOSS → 结束 → 日志)正常
- ✅ 解码谜题生成 + 自动求解通过
- ✅ 星潮事件触发 + 指示器 + Footer 联动正常
- ✅ 成就解锁 Toast 正常
- ✅ 星图标签页(9/18 天赋,6 类别)正常
- ✅ 编年史对话框(2 条目,时间轴,里程碑)正常
- ✅ lint 零错误;HTTP 200;编译 < 250ms
## 路线图进展
- [x] v0.1 MVP:放置采矿 + 解码 + 技术树 + 图谱 + 飞升
- [x] v0.1.1:解码可解性修复
- [x] v0.2:遗迹探险肉鸽
- [x] v0.2.1:程序化音频 + 成就系统
- [x] v0.3:星潮事件系统
- [x] v0.3.1:星图天文台元进程
- [x] v0.4:回响编年史
- [x] **v0.5:深空信标(每日挑战 + 本地排行榜)** ← 本轮
- [ ] v0.5+:云存档 + 云排行榜(后端同步)
- [ ] v0.5+:全 5 纪元手写叙事节点
- [ ] 后续:socket 多人同步星潮
## 下一阶段优先
1. 云存档 + 云排行榜(需后端 API,可将本地 localStorage 同步到服务端)
2. 全 5 纪元手写叙事节点(替换模板化 lore)
3. socket 多人同步星潮(全局事件,所有在线玩家同时经历)
+309
View File
@@ -0,0 +1,309 @@
# 回响星核 v0.14 — 放置系统(Idle Operations)设计文档
> 版本:v0.14 · 日期:2026-01-25
> 核心目标:让玩家打开游戏 **5 秒内** 就能看出这是个 idle game
---
## 1. 设计动机
### 1.1 用户反馈
> "你的这个我没有看到任何可放置的功能" —— 玩家反馈
虽然 v0.8 已有 `crystalsPerSec` 在后台自动累积,但 **没有任何 UI 元素明示这一事实**
- 没有「放置中」徽章
- 没有可视化自动采矿机
- 没有定时工程项目
- 离线收益弹窗只在重新加载页面时触发,玩家日常游玩看不到
放置游戏的灵魂是「关掉也在进步」。如果玩家看不到这一点,就等于没有放置玩法。
### 1.2 设计目标
1. **可见性**5 秒内让玩家意识到 idle 属性
2. **决策性**:放置不只是数字增长,要有玩家选择
3. **节奏感**:3 层 idle 节奏(被动产能 / 放置工程 / 离线收益)协同
4. **健康性**:避免 24h 挂机的极端沉迷
---
## 2. 三层 Idle 节奏
### 节奏 1:被动产能(Passive Generation
- **载体**:采矿无人机舰队(Miner Fleet)
- **来源**:每解锁一个采矿分支技术节点 → 加入一架无人机
- **行为**24h 持续产出晶体,无需任何操作
- **限制**:受 `crystalCap` 限制,仓库满后停止
- **离线**:按 `offlineEff`(默认 50%)持续累积,上限 8h
#### 采矿分支技术 → 无人机映射
| 技术节点 | 无人机名 | 输出/s | 解锁成本(洞见) |
|---------|---------|--------|-----------------|
| `min_1` | ⛏️ 谐振钻头 | +0.6 | 5 |
| `min_2` | 🌋 深层矿脉 | +1.6 | 24 |
| `min_3` | 🤖 自治机群 | +5 | 120 |
| `min_4` | 💠 量子采掘矩阵 | +12 | 400 |
| `min_5` | ☀️ 戴森星核 | +30 | 1200 |
无采矿技术时显示「谐振钻头(休眠中)」占位卡片,引导玩家解锁采矿分支。
---
### 节奏 2:放置工程(Idle Projects
#### 2.1 工程列表
| ID | 名称 | 时长 | 奖励 | 解锁条件 | 图标 | 色 |
|----|------|------|------|---------|------|-----|
| `idle_scan` | 深空勘探扫描 | 60s | ✦ +8 洞见 | 产能 ≥ 0.5/s | 🛰️ | emerald |
| `idle_refine` | 晶体精炼阵列校准 | 180s | ⇪ +0.3/s 永久 | 产能 ≥ 1/s | ⚙️ | rose |
| `idle_archive` | 遗迹碎片整理 | 600s | ✦ +20 洞见 + 📜 随机碎片 | 飞升 ≥ 1 OR 产能 ≥ 5/s | 📜 | amber |
| `idle_anchor` | 维度锚点部署 | 1200s | ⚡ +2 能量 + ☄ +5 接触进度 | 飞升 ≥ 1 | ⚓ | fuchsia |
| `idle_drones` | 无人机群扩编 | 1800s | 💎 +500 晶体 + ⇪ +2/s 永久 | 产能 ≥ 10/s | 🛸 | emerald |
| `idle_resonance` | 跨维度谐振标定 | 3600s | ✦ +100 洞见 + ☄ +15 接触 + ⇪ +8/s 永久 | 飞升 ≥ 2 | 🌌 | fuchsia |
#### 2.2 槽位机制
- **3 个并行槽位**:可同时运行 3 个工程
- **不重复**:同一工程不能在多个槽位同时运行
- **完成需手动领取**:避免"打开就有奖励"的条件反射
- **取消不退时间**:误取消的代价
- **离线推进**:关闭浏览器后工程继续倒计时
#### 2.3 奖励类型说明
| 奖励 | 含义 | 设计意图 |
|------|------|---------|
| 晶体 | 立即加到 `crystals` | 短期反馈 |
| 洞见 | 立即加到 `insights` | 用于解锁技术 |
| 能量 | 加到 `energy`(受 `energyMax` 上限) | 探险资源 |
| 接触进度 | 加到 `contact`0-100 | 推进飞升 |
| **永久产能加成** | 加到 `idlePermanentBonus`,影响 `recomputeStats` | 长期投资 |
| 随机碎片 | 从 `FRAGMENTS` 中随机解锁一个未拥有 | 叙事推进 |
---
### 节奏 3:离线收益(Offline Gains
#### 3.1 公式
```
gain = crystalsPerSec × elapsed × offlineEff
elapsed = min(realElapsed, 8h)
```
#### 3.2 解锁路径
- 初始 `offlineEff = 0.5`50% 离线效率)
- 通过技术树 `offlineEff` 类效果提升
- 通过星图天赋 `offlineEffBonus` 提升
- 上限 100%
#### 3.3 8h 上限理由
- 参考 GDC 2016 Anthony Pecorella "Quest for Progress"Egg Inc 2h 硬 cap 导致玩家流失
- 但完全无 cap 会导致 24h 挂机成为最优策略,破坏短会话体验
- 8h 折中:覆盖一个工作日/一夜,但不鼓励全天挂机
#### 3.4 离线报告弹窗
重新加载页面时,若 `elapsed > 60s`,弹出 `OfflineReportDialog`
- 「你离开了 X 小时 Y 分」
- 「星核为你产出 Z 颗晶体」
- 「产能 /s · 离线效率 N%」
- 「是否触达 8h 上限」标志
---
## 3. UI 设计
### 3.1 始终可见的 Idle 徽章(Header
位置:顶部 Header,紧邻「巡航」按钮左侧
状态:
- 产能 > 0`● 放置中 +X.X/s`(绿点脉动)
- 产能 = 0`○ 休眠中`(灰点静止)
悬停 Tooltip 显示明细:
```
基础产能 8.2/s
永久加成 +1.5/s
离线效率 75%
完成工程 12 次
```
点击徽章 → 切换到「放置」标签。
### 3.2 默认标签页 = 放置
首次打开游戏,默认 tab 是「放置」(v0.13 前是「探险」或「技术」)。
返回玩家若有进行中探险或待选天赋,自动切回原默认。
### 3.3 放置标签页四段式布局
```
┌─────────────────────────────────────────┐
│ Section 1: 放置收益概览 │
│ [放置产能] [永久加成] [完成工程] │
│ [━━━━━━━ 离线效率 50% · 上限 8h ━━━━━━] │
├─────────────────────────────────────────┤
│ Section 2: 采矿无人机舰队 │
│ [⛏️ 钻头] [🌋 矿脉] [🤖 机群] ... │
├─────────────────────────────────────────┤
│ Section 3: 放置工程槽位 │
│ [槽位1: 运行中 47s ▓▓▓░] [槽位2: 空] [槽位3: 空] │
├─────────────────────────────────────────┤
│ Section 4: 可派遣工程 │
│ [🛰️ 深空勘探扫描 1m +8] [⚙️ 精炼校准 3m +0.3/s] │
│ [📜 碎片整理 10m +20] │
│ │
│ 未解锁工程(3): │
│ ⚓ 维度锚点部署 · 需 飞升≥1 │
│ 🛸 无人机群扩编 · 需 产能≥10/s │
│ 🌌 跨维度谐振标定 · 需 飞升≥2 │
└─────────────────────────────────────────┘
```
### 3.4 IdleProjectBar(晶体球下方)
位置:左侧晶体球区,`CrystalYieldPanel` 下方
显示:当前运行的工程迷你 pill
```
🛰️ 深空勘探扫描 47s ▓▓▓░░░░░
⚙️ 晶体精炼校准 2m 13s ▓▓░░░░░░
```
无运行工程时返回 `null`(不占位)。
---
## 4. 技术实现
### 4.1 数据结构
```typescript
// types.ts
interface IdleProjectDef {
id: string;
name: string;
desc: string;
durationSec: number;
reward: { crystals?; insights?; energy?; contact?; crystalsPerSecPermanent?; randomFragment?; };
minAscensions?: number;
minCrystalsPerSec?: number;
icon: string;
color: "emerald" | "rose" | "amber" | "fuchsia";
order: number;
}
interface IdleProjectSlot {
projectId: string;
startedAt: number;
finishesAt: number;
remainingSec: number;
completed: boolean;
}
// GameState 新增 4 字段
idleProjectSlots: (IdleProjectSlot | null)[]; // 3 槽
idleProjectHistory: { projectId: string; finishedAt: number }[]; // 最近 50 条
idlePermanentBonus: number;
idleStats: { projectsCompleted: number; crystalsFromIdle: number };
```
### 4.2 Store actions
- `startIdleProject(slotIndex, projectId)` — 校验 + 创建槽位
- `cancelIdleProject(slotIndex)` — 置空槽位
- `claimIdleProject(slotIndex)` — 应用奖励 + toast + 置空槽位
- `tickIdleProjects(now)` — 推进剩余时间,标记完成
### 4.3 引擎集成
`recomputeStats()` 末尾添加:
```typescript
crystalsPerSec += state.idlePermanentBonus ?? 0;
```
这让永久产能加成真正生效。
### 4.4 飞升处理
`performPrestige()` 重置 `idleProjectSlots = [null, null, null]`(不跨周目继承进行中工程),但保留 `idlePermanentBonus` / `idleProjectHistory` / `idleStats`(跨周目永久)。
### 4.5 存档迁移
persist 添加 `version: 1` + `migrate` 函数,为旧存档补全 4 个新字段:
```typescript
idleProjectSlots: (s.idleProjectSlots as unknown[]) ?? [null, null, null],
idleProjectHistory: (s.idleProjectHistory as unknown[]) ?? [],
idlePermanentBonus: (s.idlePermanentBonus as number) ?? 0,
idleStats: (s.idleStats as unknown) ?? { projectsCompleted: 0, crystalsFromIdle: 0 },
```
旧存档零崩溃风险。
### 4.6 主循环集成
`useGameLoop` 每 250ms tick
1. `tick(now)` — 产能 + 自动产晶体
2. `autoDecodeTick()` — 自动解码
3. `tickTide(now)` — 星潮事件
4. **`tickIdleProjects(now)` — v0.14 放置工程进度**(新增)
5. `checkAchievements()` — 每秒一次
页面可见性切回时也调用 `tickIdleProjects(now)`
---
## 5. 平衡考虑
### 5.1 永久产能加成可能过强
- `idle_resonance` +8/s 永久,多次完成可能让玩家飞快积累
- **v0.15 待办**:加飞升次数 softcap,例如 `effectiveBonus = idlePermanentBonus × sqrt(ascensions + 1) / (ascensions + 1)`
### 5.2 工程时长曲线
- 60s / 180s / 600s / 1200s / 1800s / 3600s
- 短工程奖励低但灵活(适合短会话)
- 长工程奖励高但需等待(适合过夜挂机)
- 玩家自主选择节奏
### 5.3 解锁条件
- 0 飞升 + 0.5/s:新手 5 分钟内可解锁第一个
- 1 飞升:玩家完成首次飞升后解锁中段
- 2 飞升 + 10/s:长线目标
---
## 6. 验证记录
### 6.1 Lint
- ✅ 零错误零警告
### 6.2 Dev 服务器
- ✅ HTTP 200
- ✅ 无运行时错误
### 6.3 Agent Browser 端到端测试
1. ✅ 默认 tab 是「放置」
2. ✅ Header 显示「放置中 +0.4/s」徽章
3. ✅ 4 段面板完整渲染
4. ✅ 买 min_1+min_2 后产能 2.6/s,解锁 idle_scan + idle_refine
5. ✅ 点击「派遣至槽位 1」→ 槽位显示运行中 60s + 进度条 + 取消按钮
6. ✅ IdleProjectBar 同步显示迷你 pill
7. ✅ 快进完成后标记 completed=true
8. ✅ 领取后 insights +8、projectsCompleted 0→1、槽位清空
### 6.4 Sticky Footer 验证
- ✅ Header `position: sticky; top: 0`
- ✅ Footer `position: sticky; bottom: 0; margin-top: auto`
- ✅ 内容超出视口时 footer 被自然推下(无浮动空隙)
---
## 7. 未解决问题 / 后续
- 🟡 `crystalsFromIdle` / `idleProjectHistory` 已在 store 累加但 UI 未展示——v0.15 可在统计 tab 补
- 🟡 永久产能加成可能需要飞升次数 softcap
- 🟡 缺「批量领取全部」按钮(玩家有 3 个槽位都完成时方便)
- 🟡 缺工程完成 toast 通知(目前只能在面板内看到「待领取」标志)
- 🟢 v0.15 可加 Dark Souls 式 loreFragments 多层文本,让"遗迹碎片整理"工程的随机碎片奖励更有叙事感
---
## 8. 设计哲学总结
> "Incremental games are very raw. They're about relating to a process and becoming obsessed with its growth, like watching something you planted in the ground, through a time-lapse camera, grow into a tree." —— Clark (via Frank Lantz, New Yorker 2019)
回响星核 v0.14 的放置系统不只是把数字挂机增长,而是让玩家**看到**这个过程:
- 采矿无人机像植物在土里生长
- 放置工程像种子在发芽
- 离线收益像收获时节
玩家不再只是「数字变大」,而是「亲手布置了一个会自动运转的考古基地,然后离开,让它在自己不在的时候继续工作」。
这是 idle game 的本质:**让玩家信任系统会在他不在时为他工作**。
+214
View File
@@ -0,0 +1,214 @@
# v0.15 — 叙事深化(loreLayers+ 放置平衡(softcap+ UX 增强
> 回响星核 / Echo Nexus · v0.15 设计文档
> 对应 worklog `Task ID: v0.15-release`
---
## 一、动机
v0.14 上线了完整的放置系统(6 工程 + 永久加成 + idle 徽章),但留下两个问题:
1. **平衡性漏洞**`idle_resonance` 工程每次完成 +8/s 永久加成,玩家可反复派遣导致滚雪球,几小时后产能爆炸。
2. **叙事单薄**8 个记忆碎片只有一层 `echo` 文本,解锁后即"读完",缺乏 Dark Souls / Outer Wilds 式的碎片叙事深度——这是项目的**差异化竞争力**。
v0.15 用两个并行子代理一次性解决:
- **2-a(平衡 + UX**:永久加成 softcap + 批量领取 + 完成 toast + StatsPanel idle 统计
- **2-b(叙事深化)**:8 碎片 × 2 层 = 16 层 loreLayers 多层文本 + Codex 分层渲染
---
## 二、平衡:永久加成 Softcap
### 公式
```ts
export function applyIdleSoftcap(bonus: number): number {
if (bonus <= 30) return bonus; // 全额
if (bonus <= 100) return 30 + (bonus - 30) * 0.5; // 超出部分 ×0.5
return 65 + (bonus - 100) * 0.25; // 超出部分 ×0.25
}
```
### 临界值
| 原始 bonus | 有效 bonus | 衰减率 |
|-----------|-----------|--------|
| 30 | 30 | 0% |
| 50 | 40 | 20% |
| 100 | 65 | 35% |
| 200 | 90 | 55% |
| 500 | 152.5 | 69.5% |
### 设计意图
- **30 以下不衰减**:新手期 + 偶尔完成 `idle_refine`+0.3/s)不受惩罚
- **30-100 线性半衰**:中段玩家反复完成 `idle_drones`+2/s)开始感到边际递减
- **100 以上严重衰减**:硬核玩家刷 `idle_resonance`(+8/s)收益大幅缩水,但仍非零(保留进度感)
- **永不归零**`applyIdleSoftcap(bonus) → ∞` 时仍趋向无穷,只是增速极慢——避免"刷到上限"的挫败感
### UI 展示
`IdleOperationsPanel` Section 1「永久加成」StatTile
- 有效值永远显示(如 `+40/s`
- 当 `有效 < 原始` 时,下方加 amber 副行:`有效 +40/s(原始 +50/s · 衰减中)`
---
## 三、叙事深化:loreLayers 多层碎片文本
### 类型扩展
```ts
export interface FragmentLoreLayer {
layer: 1 | 2; // 1=中层,2=深层
title: string; // "隐秘注脚" / "深层回响"
text: string; // 60-150 字叙事文本
unlockHint: string; // "飞升 1 次后揭示"
unlock: FragmentLoreUnlock;
}
export interface FragmentLoreUnlock {
kind: "ascensions" | "totalDecoded" | "contact" | "fragmentCount" | "bossKills";
value: number;
}
```
### 5 种解锁条件
| kind | 含义 | 典型阈值 |
|------|------|---------|
| `ascensions` | 飞升次数 | 1 / 2 / 3 |
| `totalDecoded` | 累计解码晶体数 | 20 / 40 / 60 |
| `contact` | 接触进度 | 50 / 80 |
| `fragmentCount` | 已解锁碎片数 | 4 / 6 |
| `bossKills` | 累计击杀 BOSS | 3 / 8 |
### 5 条叙事暗线(贯穿 16 层)
1. **追逐者**:以太族并非自然消亡,而是被虚空中"某种东西"追逐而集体飞升
2. **回响同源**:玩家舰载 AI"回响"的频段直接复制自以太族议会档案——玩家其实是以太族的延续/入口
3. **信任即引诱**:第一纪元的"信任后来者"实为"引诱后来者替他们完成未竟之事"
4. **飞升是单向门**:每次飞升都在消耗什么,不是奖赏
5. **回归的不是他们**:接触进度满 80 时回归的,是"借他们形状的某种东西",请玩家别回应
### 示例(f1_1 第一缕谐振)
**L0 表层**(默认):
> 在被称作「寂灭前夜」的年代,以太族第一次捕捉到来自虚空深处的谐振——那不是声音,而是存在的回声。他们将之记录在第一颗记忆晶体里。
**L1 隐秘注脚**(飞升 ≥1):
> 档案的边缘有一行几乎被擦去的批注:「那并非来自虚空——而是来自我们身后。我们一直不敢回头。」
**L2 深层回响**(累计解码 ≥40):
> 你终于明白:那缕谐振的频率,与你的舰载 AI「回响」的核心频段完全一致。你不是在考古——你是在被召唤。而你身后的东西,一直在等。
### Codex 分层渲染
| 层级 | 颜色 | 标题 | 未解锁状态 |
|------|------|------|-----------|
| L0 表层 | emerald | 表层回响 | 永远显示 |
| L1 中层 | amber | 隐秘注脚 | `???` + `blur-sm` 全息乱码 + `🔒 unlockHint` |
| L2 深层 | fuchsia | 深层回响 | `???` + `blur-sm` 全息乱码 + `🔒 unlockHint` |
顶部统计:`深层叙事 X / 16 已解锁`
列表卡片:已解锁深层文本的碎片标题旁显示 amber/fuchsia glow 小圆点
---
## 四、UX 增强
### 4.1 批量领取全部
`claimAllIdleProjects()` action
- 遍历 3 槽位,对所有 `completed === true` 执行奖励逻辑
- 单次 `set()` + 单次 `syncStats()`(避免多次 re-render
- 无可领取 → 直接 return(不弹 toast
- 成功 → 汇总 toast `✦ 批量领取完成 · 共领取 N 项工程 · +X 晶体 · +Y 洞见 · ...`
UISection 3 标题右侧,当存在 completed slot 时显示 emerald「✓ 领取全部」按钮(h-5 px-2 text-[10px]
### 4.2 工程完成 toast
`tickIdleProjects` 检测状态转变瞬间(旧 `!completed` → 新 `completed`):
- 收集 `justCompletedProjects` 数组
- 循环外单次 `toast({ title: "✦ 放置工程完成", description: "${icon} ${name} · 可前往「放置」标签领取" })`
- 避免每 250ms tick 重复弹窗
### 4.3 StatsPanel idle 统计
`page.tsx` 内联函数抽出为独立组件 `StatsPanel.tsx`
- `useShallow` 精细订阅 24 字段
- 新增 idle 统计行:
- 放置永久加成 `+X /s`
- 完成放置工程 `N 项`
- 放置产出晶体 `formatNum(N)`
- 放置工程历史 `N 条`(最近 50 条,跨周目)
---
## 五、技术实现
### 修改文件清单(8 个)
**子代理 2-a5 文件):**
- `src/lib/game/engine.ts` — 新增 `applyIdleSoftcap` 导出 + `recomputeStats` 调用
- `src/store/gameStore.ts``GameActions``claimAllIdleProjects` 签名 + `tickIdleProjects` 改写 + 新增 action
- `src/components/game/IdleOperationsPanel.tsx` — softcap 副行 + 领取全部按钮 + StatTile 扩展
- `src/components/game/StatsPanel.tsx` — 新建独立组件
- `src/app/page.tsx` — 最小集成(删除内联 + import)
**子代理 2-b3 文件):**
- `src/lib/game/types.ts``FragmentLoreLayer` / `FragmentLoreUnlock` 接口 + `Fragment.loreLayers` 字段
- `src/lib/game/config.ts` — FRAGMENTS 全部填充 loreLayers + `isLoreLayerUnlocked` 函数
- `src/components/game/Codex.tsx` — useShallow + 顶部统计 + 列表小圆点 + 3 层分层渲染弹层
### 并行零冲突
两个子代理修改文件严格不重叠:
- 2-a 不碰 `types.ts` / `config.ts` / `Codex.tsx`
- 2-b 不碰 `engine.ts` / `gameStore.ts` / `IdleOperationsPanel.tsx` / `StatsPanel.tsx`
---
## 六、验证
- ✅ `bun run lint` 零错误零警告
- ✅ HTTP 200dev.log 无运行时错误
- ✅ agent-browser 端到端 smoke test
- softcappermBonus=50 → 有效 40UI 显示"衰减中" ✓
- 批量领取:permBonus 0→0.3, projectsCompleted 1→2 ✓
- 完成 toasttickIdleProjects 触发 ✓
- Codex 分层:L0 显示 / L1·L2 模糊 → ascensions=1 解锁 L1 → totalDecoded=50 解锁 L2 ✓
- 深层叙事计数 0→2→6 随条件递增 ✓
- StatsPanel idle 4 行统计全显示 ✓
---
## 七、哲学
### 碎片叙事的留白
回响星核的叙事不靠过场动画或长篇对话,而是**让玩家自己拼凑**。每个碎片的多层文本设计遵循:
1. **表层温和**:第一遍读像抒情散文,不带压迫感
2. **中层矛盾**:揭示表层没说的东西,制造认知裂缝
3. **深层颠覆**:推翻表层认知,但留有解读空间(不直接说"怪物是谁")
### 平衡与保留
softcap 的设计哲学:**衰减但不封顶**。
- 衰减:防止滚雪球破坏游戏节奏
- 不封顶:尊重硬核玩家的投入,即使边际收益递减,进度感始终存在
这与飞升系统的"单向门"叙事呼应——每次进步都有代价,但绝非徒劳。
---
## 八、下一步(v0.16+
- 🟡 loreLayer "已读"标记(需改 gameStore,本轮避开边界冲突)
- 🟡 解码阈值释放叙事碎片 + 星潮三频段切换
- 🟡 健康设计:每日软上限 + "星核建议休眠"提醒
- 🟡 移动端 375px Codex 弹层可读性实测
Submodule
+1
Submodule docs/repo added at a02b6191c5
+1
View File
@@ -0,0 +1 @@
Here are all the generated files.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Some files were not shown because too many files have changed in this diff Show More