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:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
// 回响星核 / Echo Nexus — 遗迹探险面板(v0.2 肉鸽系统)
|
||||
import { useState } from "react";
|
||||
// 回响星核 / Echo Nexus — 遗迹探险面板(v0.2 肉鸽系统,v0.9 工单修复)
|
||||
import { useState, useEffect } from "react";
|
||||
import { useGameStore } from "@/store/gameStore";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { sfx } from "@/hooks/useAudio";
|
||||
@@ -42,20 +42,30 @@ export function ExpeditionPanel() {
|
||||
const lastEnergyTick = useGameStore((s) => s.lastEnergyTick);
|
||||
const expeditionLog = useGameStore((s) => s.expeditionLog);
|
||||
const totalExpeditions = useGameStore((s) => s.totalExpeditions);
|
||||
const lastExpeditionSummary = useGameStore((s) => s.lastExpeditionSummary);
|
||||
// v0.8.1 动态能量恢复间隔(技术 + 探索力属性缩短)
|
||||
const regenIntervalSec = useGameStore((s) => computeEnergyRegenInterval(s));
|
||||
const startExpedition = useGameStore((s) => s.startExpedition);
|
||||
const resolveCurrentNode = useGameStore((s) => s.resolveCurrentNode);
|
||||
const advanceNode = useGameStore((s) => s.advanceNode);
|
||||
const abortExpedition = useGameStore((s) => s.abortExpedition);
|
||||
const dismissExpedition = useGameStore((s) => s.dismissExpedition);
|
||||
const { toast } = useToast();
|
||||
const [lastLog, setLastLog] = useState<string | null>(null);
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
// v0.9 工单修复:能量恢复实时倒计时(每秒刷新)
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setNow(Date.now()), 1000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
// 能量恢复进度(v0.8.1 动态间隔)
|
||||
const now = Date.now();
|
||||
// 能量恢复进度(v0.8.1 动态间隔,v0.9 实时刷新)
|
||||
const regenMs = regenIntervalSec * 1000;
|
||||
const regenProgress = energy >= energyMax ? 100 : Math.min(100, ((now - lastEnergyTick) / regenMs) * 100);
|
||||
const elapsed = now - lastEnergyTick;
|
||||
const regenProgress = energy >= energyMax ? 100 : Math.min(100, (elapsed / regenMs) * 100);
|
||||
const regenBoosted = regenIntervalSec < EXPEDITION_CONFIG.energyRegenSec;
|
||||
const nextEnergyInSec = energy >= energyMax ? 0 : Math.max(0, Math.ceil((regenMs - elapsed) / 1000));
|
||||
const canStart = energy >= EXPEDITION_CONFIG.energyCost;
|
||||
|
||||
const handleStart = () => {
|
||||
const res = startExpedition();
|
||||
@@ -100,6 +110,9 @@ export function ExpeditionPanel() {
|
||||
|
||||
// ===== 无活跃探险:展示入口 =====
|
||||
if (!activeExpedition || activeExpedition.finished) {
|
||||
// v0.9 工单修复:如果探险刚结束(finished 且有摘要),显示结算面板
|
||||
const justFinished = activeExpedition?.finished && lastExpeditionSummary &&
|
||||
Date.now() - lastExpeditionSummary.finishedAt < 60000;
|
||||
return (
|
||||
<div className="flex flex-col gap-3 h-full">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -110,6 +123,40 @@ export function ExpeditionPanel() {
|
||||
<span className="text-[10px] text-muted-foreground">已探索 {totalExpeditions} 次</span>
|
||||
</div>
|
||||
|
||||
{/* v0.9 工单修复:上次探险结果摘要 */}
|
||||
{lastExpeditionSummary && (
|
||||
<div
|
||||
className={`rounded-xl border p-2.5 relative overflow-hidden ${
|
||||
lastExpeditionSummary.result === "victory"
|
||||
? "border-emerald-400/40 bg-emerald-950/20"
|
||||
: lastExpeditionSummary.result === "defeat"
|
||||
? "border-rose-400/40 bg-rose-950/20"
|
||||
: "border-amber-400/30 bg-amber-950/10"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-[11px] font-semibold flex items-center gap-1">
|
||||
{lastExpeditionSummary.result === "victory" ? "✦ 上次探险:胜利" : lastExpeditionSummary.result === "defeat" ? "✕ 上次探险:失败" : "→ 上次探险:撤退"}
|
||||
</span>
|
||||
<span className="text-[9px] text-muted-foreground font-mono">{lastExpeditionSummary.nodeCount} 节点</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2.5 text-[10px]">
|
||||
{lastExpeditionSummary.rewards.crystals > 0 && (
|
||||
<span className="text-emerald-300">+{formatNum(lastExpeditionSummary.rewards.crystals)} 晶体</span>
|
||||
)}
|
||||
{lastExpeditionSummary.rewards.insights > 0 && (
|
||||
<span className="text-amber-300">+{lastExpeditionSummary.rewards.insights} 洞见</span>
|
||||
)}
|
||||
{lastExpeditionSummary.rewards.contact > 0 && (
|
||||
<span className="text-fuchsia-300">+{lastExpeditionSummary.rewards.contact.toFixed(1)} 接触</span>
|
||||
)}
|
||||
{lastExpeditionSummary.rewards.crystals === 0 && lastExpeditionSummary.rewards.insights === 0 && (
|
||||
<span className="text-muted-foreground/60">无奖励</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 能量条 */}
|
||||
<div className="rounded-xl border border-amber-400/20 bg-amber-950/20 p-3">
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
@@ -127,8 +174,10 @@ export function ExpeditionPanel() {
|
||||
value={regenProgress}
|
||||
className="h-1.5 bg-black/40 [&>div]:bg-gradient-to-r [&>div]:from-amber-500 [&>div]:to-yellow-300"
|
||||
/>
|
||||
<p className="text-[10px] text-muted-foreground/70 mt-1">
|
||||
下一点能量约 {Math.ceil((regenMs - (now - lastEnergyTick)) / 1000)}s 后恢复
|
||||
<p className="text-[10px] text-muted-foreground/70 mt-1 flex items-center gap-1">
|
||||
<span>下一点能量</span>
|
||||
<span className="text-amber-300 font-mono font-semibold">{nextEnergyInSec}s</span>
|
||||
<span>后恢复</span>
|
||||
{regenBoosted && (
|
||||
<span className="text-emerald-400/80 ml-1">⚡ {regenIntervalSec.toFixed(0)}s/点 (已加速)</span>
|
||||
)}
|
||||
@@ -140,35 +189,32 @@ export function ExpeditionPanel() {
|
||||
</div>
|
||||
|
||||
{/* 出发按钮 */}
|
||||
<div className="flex-1 flex flex-col items-center justify-center gap-4 rounded-2xl border border-dashed border-amber-400/20 bg-black/30 p-6 relative overflow-hidden">
|
||||
<div className="flex-1 flex flex-col items-center justify-center gap-3 rounded-2xl border border-dashed border-amber-400/20 bg-black/30 p-4 relative overflow-hidden">
|
||||
<div className="absolute -top-10 -right-10 h-32 w-32 rounded-full bg-amber-500/10 blur-2xl" />
|
||||
<div className="relative">
|
||||
<div className="h-20 w-20 rounded-full bg-gradient-to-br from-amber-500/30 to-rose-500/20 flex items-center justify-center mx-auto" style={{ boxShadow: "0 0 30px rgba(251,191,36,0.3)" }}>
|
||||
<Rocket className="h-9 w-9 text-amber-300" />
|
||||
<div className={`h-16 w-16 rounded-full bg-gradient-to-br from-amber-500/30 to-rose-500/20 flex items-center justify-center mx-auto ${canStart ? "animate-pulse" : ""}`} style={{ boxShadow: "0 0 30px rgba(251,191,36,0.3)" }}>
|
||||
<Rocket className="h-7 w-7 text-amber-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center space-y-1">
|
||||
<p className="text-sm font-medium text-amber-100">深入以太遗迹</p>
|
||||
<p className="text-[11px] text-muted-foreground/80 max-w-[220px]">
|
||||
派出探险队进入程序化生成的遗迹路径,沿途遭遇战斗、宝藏、抉择与解谜,击败终点 BOSS 获取丰厚奖励。
|
||||
程序化生成的遗迹路径,沿途遭遇战斗、宝藏、抉择与解谜,击败终点 BOSS 获取丰厚奖励。
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleStart}
|
||||
disabled={energy < EXPEDITION_CONFIG.energyCost}
|
||||
disabled={!canStart}
|
||||
className="bg-gradient-to-r from-amber-600 to-rose-500 hover:from-amber-500 hover:to-rose-400 text-white border-0"
|
||||
>
|
||||
<Rocket className="h-4 w-4 mr-1.5" />
|
||||
出发(消耗 {EXPEDITION_CONFIG.energyCost} 能量)
|
||||
{canStart ? `出发(消耗 ${EXPEDITION_CONFIG.energyCost} 能量)` : `能量不足(${nextEnergyInSec}s 后恢复)`}
|
||||
</Button>
|
||||
{energy < EXPEDITION_CONFIG.energyCost && (
|
||||
<p className="text-[10px] text-rose-300/80">能量不足,请等待恢复</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 探险日志 */}
|
||||
{expeditionLog.length > 0 && (
|
||||
<div className="rounded-xl border border-white/5 bg-black/20 p-2 max-h-32 overflow-y-auto echo-scroll">
|
||||
<div className="rounded-xl border border-white/5 bg-black/20 p-2 max-h-28 overflow-y-auto echo-scroll">
|
||||
<div className="text-[10px] text-muted-foreground/60 mb-1 px-1">最近探险记录</div>
|
||||
{expeditionLog.slice(0, 4).map((entry, i) => (
|
||||
<div key={i} className="text-[10px] text-muted-foreground/70 px-1 py-0.5 border-b border-white/5 last:border-0">
|
||||
@@ -178,6 +224,18 @@ export function ExpeditionPanel() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* v0.9 工单修复:刚结束的探险,显示"返回"按钮 */}
|
||||
{justFinished && (
|
||||
<Button
|
||||
onClick={() => dismissExpedition()}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 text-[11px] border-amber-400/30 text-amber-200"
|
||||
>
|
||||
收起结算,准备下次出发
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user