工单 #8 编年史上限+分页: - engine.ts: slice(-50)→slice(-200) - ChronicleDialog.tsx: 加分页(每页10条)+上一页/下一页+页码显示 工单 #5 限时挑战 (subagent 10-a): - beacon.ts: BeaconTimedChallenge + getTimedSlotKey(4h时段) + generateTimedChallenge - BeaconPanel.tsx: amber主题限时区块 + 倒计时 + <30min紧急状态 - gameStore: trackBeacon 加 timedJustCompleted + claimTimedBeacon action 工单 #3 星潮类型深化 (subagent 10-a): - starTide.ts: +3种星潮 surge(emerald)/eclipse(rose)/prism(fuchsia) - TideModifiers: +targetLenBonus/bossWinRateBonus/autoDecodeIntervalMult - decode.ts: generatePuzzle 加 targetLenBonus 参数 - achievements: ach_tides_all 阈值 6→9 工单 #4/P2 云排行榜 (subagent 10-b): - mini-services/leaderboard-service/ (端口3030, Hono+bun, 内存1000条) - API: GET/POST /api/leaderboard + /stats + CORS + 防刷 - beacon.ts: fetchCloudLeaderboard/submitCloudScore - BeaconPanel: 本地Top20/全球Top100 双tab + YOU徽章高亮 工单 #9 手写叙事 (subagent 10-c): - chronicle.ts: EPOCH_LORE 5纪元×3节点=15段手写叙事(80-150字/段) - buildLore 优先手写节点, fallback 模板, 11个变量替换 P3 socket 多人星潮 (subagent 10-c): - mini-services/star-tide-service/ (端口3031, socket.io) - 每10-15min广播global-tide, 60s持续, 6种类型权重 - useGlobalTide hook + triggerGlobalTide action - StarTideIndicator 加 🌐 全球星潮标记 P2 UI打磨: - page.tsx: CrystalOrb 区加装饰全息环(3层旋转) + 四角标记 + 顶部状态条 + 底部铭文 QA: lint零错误 + dev HTTP200 + VLM 8/10 + 2个mini-service运行中(3030/3031)
171 lines
5.8 KiB
TypeScript
Executable File
171 lines
5.8 KiB
TypeScript
Executable File
"use client";
|
||
// 回响星核 / Echo Nexus — 星潮事件 UI(横幅 + 通知 + 背景叠层)
|
||
import { useEffect, useState } from "react";
|
||
import { useGameStore } from "@/store/gameStore";
|
||
import { useToast } from "@/hooks/use-toast";
|
||
import { sfx } from "@/hooks/useAudio";
|
||
import { TIDE_EVENTS, TIDE_CONFIG } from "@/lib/game/starTide";
|
||
|
||
/** 星潮事件通知消费者:监听 _tideEvents 队列,弹 Toast + 播音效 */
|
||
export function StarTideNotifier() {
|
||
const events = useGameStore((s) => s._tideEvents);
|
||
const consume = useGameStore((s) => s.consumeTideEvents);
|
||
const { toast } = useToast();
|
||
|
||
useEffect(() => {
|
||
if (events.length === 0) return;
|
||
const items = consume();
|
||
for (const ev of items) {
|
||
const globalTag = ev.isGlobal ? "🌐 " : "";
|
||
if (ev.started) {
|
||
const meta = TIDE_EVENTS[ev.started];
|
||
sfx("tideStart");
|
||
toast({
|
||
title: `${globalTag}${meta.icon} ${ev.isGlobal ? "全球星潮" : "星潮降临"}:${meta.name}`,
|
||
description: ev.isGlobal
|
||
? `全球玩家同步经历 · ${meta.desc}`
|
||
: meta.desc,
|
||
});
|
||
}
|
||
if (ev.ended) {
|
||
const meta = TIDE_EVENTS[ev.ended];
|
||
if (ev.silenceCompensation && ev.silenceCompensation > 0) {
|
||
sfx("techBuy");
|
||
toast({
|
||
title: `${globalTag}${meta.icon} 寂静期消散`,
|
||
description: `虚空回赠 ${ev.silenceCompensation} 洞见。`,
|
||
});
|
||
} else {
|
||
sfx("tideEnd");
|
||
toast({
|
||
title: `${globalTag}${meta.icon} ${meta.name}结束`,
|
||
description: ev.isGlobal
|
||
? "全球星潮退去,物理法则恢复。"
|
||
: "星潮退去,物理法则恢复。",
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}, [events, consume, toast]);
|
||
|
||
return null;
|
||
}
|
||
|
||
/** 顶部星潮指示器(活跃时显示倒计时芯片) */
|
||
export function StarTideIndicator() {
|
||
const activeTide = useGameStore((s) => s.activeTide);
|
||
const globalTide = useGameStore((s) => s.globalTide);
|
||
const [, force] = useState(0);
|
||
|
||
// 每秒刷新倒计时
|
||
useEffect(() => {
|
||
if (!activeTide) return;
|
||
const id = setInterval(() => force((n) => n + 1), 500);
|
||
return () => clearInterval(id);
|
||
}, [activeTide]);
|
||
|
||
if (!activeTide) return null;
|
||
const meta = TIDE_EVENTS[activeTide.type];
|
||
const remaining = Math.max(0, activeTide.endsAt - Date.now());
|
||
const secs = Math.ceil(remaining / 1000);
|
||
// 全球星潮持续 60s,本地 75s —— 用实际 endsAt-startedAt 计算 progress,避免越界
|
||
const duration = globalTide
|
||
? globalTide.endsAt - globalTide.startedAt
|
||
: TIDE_CONFIG.duration;
|
||
const progress = Math.min(1, Math.max(0, 1 - remaining / duration));
|
||
const isGlobal = !!globalTide;
|
||
|
||
return (
|
||
<div
|
||
className={`flex items-center gap-1.5 px-2 py-1 rounded-full text-[10px] font-medium relative overflow-hidden ${
|
||
isGlobal ? "ring-1 ring-amber-300/60" : ""
|
||
}`}
|
||
style={{
|
||
background: isGlobal
|
||
? `linear-gradient(135deg, ${meta.color}26, ${meta.color}11)`
|
||
: `${meta.color}1a`,
|
||
border: `1px solid ${meta.color}${isGlobal ? "88" : "55"}`,
|
||
color: meta.color,
|
||
boxShadow: `0 0 ${isGlobal ? "16" : "10"}px ${meta.glow}`,
|
||
}}
|
||
title={isGlobal ? `🌐 全球星潮 · ${meta.desc}` : meta.desc}
|
||
>
|
||
{/* 进度环背景 */}
|
||
<div
|
||
className="absolute inset-0 opacity-20"
|
||
style={{
|
||
background: `linear-gradient(90deg, ${meta.color}40 ${progress * 100}%, transparent ${progress * 100}%)`,
|
||
}}
|
||
/>
|
||
{isGlobal && (
|
||
<span
|
||
className="relative text-[11px] leading-none animate-pulse"
|
||
aria-label="全球星潮标记"
|
||
>
|
||
🌐
|
||
</span>
|
||
)}
|
||
<span className="relative text-xs leading-none">{meta.icon}</span>
|
||
<span className="relative leading-none">{meta.name}</span>
|
||
<span className="relative font-mono leading-none opacity-80">{secs}s</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/** 全屏背景叠层:活跃星潮时给页面罩一层对应色的微光 */
|
||
export function StarTideOverlay() {
|
||
const activeTide = useGameStore((s) => s.activeTide);
|
||
const globalTide = useGameStore((s) => s.globalTide);
|
||
if (!activeTide) return null;
|
||
const meta = TIDE_EVENTS[activeTide.type];
|
||
const isGlobal = !!globalTide;
|
||
|
||
return (
|
||
<div className="fixed inset-0 pointer-events-none z-0 transition-opacity duration-1000">
|
||
{/* 顶部色带 */}
|
||
<div
|
||
className="absolute top-0 left-0 right-0 h-24 opacity-40"
|
||
style={{
|
||
background: `linear-gradient(180deg, ${meta.color}22, transparent)`,
|
||
}}
|
||
/>
|
||
{/* 底部色带 */}
|
||
<div
|
||
className="absolute bottom-0 left-0 right-0 h-24 opacity-40"
|
||
style={{
|
||
background: `linear-gradient(0deg, ${meta.color}22, transparent)`,
|
||
}}
|
||
/>
|
||
{/* 边缘呼吸光 */}
|
||
<div
|
||
className="absolute inset-0"
|
||
style={{
|
||
boxShadow: `inset 0 0 ${isGlobal ? "160" : "120"}px ${meta.glow}`,
|
||
animation: "tide-breathe 3s ease-in-out infinite",
|
||
}}
|
||
/>
|
||
{/* 全球星潮:顶部多一道琥珀色光带,强化"全球同步"视觉信号 */}
|
||
{isGlobal && (
|
||
<div
|
||
className="absolute top-0 left-0 right-0 h-1 opacity-90"
|
||
style={{
|
||
background: "linear-gradient(90deg, transparent, #fbbf24, #fcd34d, #fbbf24, transparent)",
|
||
boxShadow: "0 0 16px rgba(251,191,36,0.7)",
|
||
animation: "global-tide-sweep 2.5s linear infinite",
|
||
}}
|
||
/>
|
||
)}
|
||
<style jsx>{`
|
||
@keyframes tide-breathe {
|
||
0%, 100% { opacity: 0.5; }
|
||
50% { opacity: 0.9; }
|
||
}
|
||
@keyframes global-tide-sweep {
|
||
0% { transform: translateX(-30%); }
|
||
100% { transform: translateX(30%); }
|
||
}
|
||
`}</style>
|
||
</div>
|
||
);
|
||
}
|