v0.9 工单修复: 4 bug + 4 玩法改进

Gitea Issue #2 修复:
Bug:
1. 探险只能一次 → 能量恢复 45→20s, 初始能量 3→5, 上限 5→8
   新增 lastExpeditionSummary 显示上次结果
   新增 dismissExpedition action
2. 元素偏移/遮挡 → 标签页 grid-cols-8 → 响应式 grid-cols-4/sm:8
3. 成就UI遮挡 → max-h 440→520, min-h 提高
4. 提示框遮挡 → toast top-0→top-14 避开 header, TOAST_LIMIT 1→3

玩法改进:
1. 中央晶体球 → 下方新增 4 指标收益面板(产能/脉冲/连击/仓库)
2. 技术树 12→20 节点 (每分支 3→5 级)
3. 成就解锁 → 全屏居中特效通知 (framer-motion 弹窗)
4. 叙事融入 → 新建 narrative.ts, 脉冲/技术树注入世界观文本
This commit is contained in:
2026-06-24 00:41:16 +00:00
parent eb9836b8af
commit 44b94f9325
17 changed files with 2236 additions and 43 deletions
+37 -3
View File
@@ -127,6 +127,8 @@ interface GameActions {
resolveCurrentNode: () => ExpeditionResult | null;
advanceNode: () => void;
abortExpedition: () => void;
/** v0.9 工单修复:手动关闭已结束的探险,回到入口 */
dismissExpedition: () => void;
// 技术
buyTech: (techId: string) => boolean;
@@ -778,12 +780,17 @@ export const useGameStore = create<Store>()(
const tideIntervalMult = tideModEarly.autoDecodeIntervalMult ?? 1;
const interval = Math.max(5000, baseInterval * am.autoDecodeIntervalMult * tideIntervalMult);
if (now - s._lastAutoDecode < interval) return;
// 找一颗 T1 晶体自动解码
const idx = s.pendingCrystals.findIndex((c) => c.tier === 1);
// 找一颗 T1 晶体自动解码;v0.9 dec_5「全息解码核心」解锁后可自动解码 T2
let autoTier: CrystalTier = 1;
let idx = s.pendingCrystals.findIndex((c) => c.tier === 1);
if (idx < 0 && s.tech?.dec_5) {
idx = s.pendingCrystals.findIndex((c) => c.tier === 2);
autoTier = 2;
}
if (idx < 0) return;
const crystal = s.pendingCrystals[idx];
const tideMod = getTideModifiers(s.activeTide);
const base = decodeRewards(1, s.insightMult, s.contactRateMult);
const base = decodeRewards(autoTier, s.insightMult, s.contactRateMult);
const finalMult = tideMod.decodeRewardMult * cm.decodeRewardMult;
const rewards = {
crystals: Math.round(base.crystals * finalMult),
@@ -923,6 +930,17 @@ export const useGameStore = create<Store>()(
exp.finished = true;
}
// v0.9 工单修复:探险结束时记录摘要,用于入口界面展示
let lastExpeditionSummary = s.lastExpeditionSummary ?? null;
if (result.ended && (result.endReason === "victory" || result.endReason === "defeat")) {
lastExpeditionSummary = {
result: result.endReason,
rewards: { ...exp.rewards },
nodeCount: exp.nodes.length,
finishedAt: Date.now(),
};
}
// v0.4 编年史:击破 BOSS 时累计计数
let bossKills = s.bossKills ?? 0;
let bossKilledThisNode = false;
@@ -979,6 +997,7 @@ export const useGameStore = create<Store>()(
bossKills,
attributes: newAttributes,
attributeProgress: newProgress,
lastExpeditionSummary,
...(statsNeedResync
? syncStats({ tech: s.tech, blueprints: s.blueprints, achievements: s.achievements, activeTide: s.activeTide, constellation: s.constellation, attributes: newAttributes })
: {}),
@@ -1017,12 +1036,27 @@ export const useGameStore = create<Store>()(
rewards: "",
timestamp: Date.now(),
};
// v0.9 工单修复:记录上次探险摘要,用于入口展示
const summary = {
result: "abort" as const,
rewards: { ...exp.rewards },
nodeCount: exp.nodes.length,
finishedAt: Date.now(),
};
set({
activeExpedition: exp,
expeditionLog: [logEntry, ...s.expeditionLog].slice(0, 30),
lastExpeditionSummary: summary,
});
},
// v0.9 工单修复:手动关闭已结束探险,回到入口
dismissExpedition: () => {
const s = get();
if (!s.activeExpedition || !s.activeExpedition.finished) return;
set({ activeExpedition: null });
},
doPrestige: () => {
const s = get();
if (s.contact < CONTACT.prestigeMin) return null;