42773b7e-7147-4ad5-828d-da7fcf0290b8
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"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) {
|
||||
if (ev.started) {
|
||||
const meta = TIDE_EVENTS[ev.started];
|
||||
sfx("tideStart");
|
||||
toast({
|
||||
title: `${meta.icon} 星潮降临:${meta.name}`,
|
||||
description: meta.desc,
|
||||
});
|
||||
}
|
||||
if (ev.ended) {
|
||||
const meta = TIDE_EVENTS[ev.ended];
|
||||
if (ev.silenceCompensation && ev.silenceCompensation > 0) {
|
||||
sfx("techBuy");
|
||||
toast({
|
||||
title: `${meta.icon} 寂静期消散`,
|
||||
description: `虚空回赠 ${ev.silenceCompensation} 洞见。`,
|
||||
});
|
||||
} else {
|
||||
sfx("tideEnd");
|
||||
toast({
|
||||
title: `${meta.icon} ${meta.name}结束`,
|
||||
description: "星潮退去,物理法则恢复。",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [events, consume, toast]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 顶部星潮指示器(活跃时显示倒计时芯片) */
|
||||
export function StarTideIndicator() {
|
||||
const activeTide = useGameStore((s) => s.activeTide);
|
||||
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);
|
||||
const progress = 1 - remaining / TIDE_CONFIG.duration;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-1.5 px-2 py-1 rounded-full text-[10px] font-medium relative overflow-hidden"
|
||||
style={{
|
||||
background: `${meta.color}1a`,
|
||||
border: `1px solid ${meta.color}55`,
|
||||
color: meta.color,
|
||||
boxShadow: `0 0 10px ${meta.glow}`,
|
||||
}}
|
||||
title={meta.desc}
|
||||
>
|
||||
{/* 进度环背景 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-20"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, ${meta.color}40 ${progress * 100}%, transparent ${progress * 100}%)`,
|
||||
}}
|
||||
/>
|
||||
<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);
|
||||
if (!activeTide) return null;
|
||||
const meta = TIDE_EVENTS[activeTide.type];
|
||||
|
||||
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 120px ${meta.glow}`,
|
||||
animation: "tide-breathe 3s ease-in-out infinite",
|
||||
}}
|
||||
/>
|
||||
<style jsx>{`
|
||||
@keyframes tide-breathe {
|
||||
0%, 100% { opacity: 0.5; }
|
||||
50% { opacity: 0.9; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user