3d8566bc-063f-41e8-8934-68c9ac5b8211
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// 回响星核 / Echo Nexus — 成就系统(数据驱动)
|
||||
import type { GameState } from "./types";
|
||||
|
||||
/** 成就奖励类型 */
|
||||
export interface AchievementReward {
|
||||
crystals?: number;
|
||||
insights?: number;
|
||||
contact?: number;
|
||||
/** 永久产能加成(百分比,叠加到所有周目) */
|
||||
crystalsPerSecPct?: number;
|
||||
insightPct?: number;
|
||||
}
|
||||
|
||||
export interface Achievement {
|
||||
id: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
/** 图标 emoji(轻量,无需图标库) */
|
||||
icon: string;
|
||||
/** 颜色(用于徽章) */
|
||||
color: string;
|
||||
/** 判定函数:返回 true 表示达成 */
|
||||
check: (s: GameState) => boolean;
|
||||
reward: AchievementReward;
|
||||
/** 奖励文本(展示用) */
|
||||
rewardText: string;
|
||||
}
|
||||
|
||||
export const ACHIEVEMENTS: Achievement[] = [
|
||||
{
|
||||
id: "ach_first_pulse",
|
||||
name: "初次触碰",
|
||||
desc: "发起第一次脉冲扫描",
|
||||
icon: "✦",
|
||||
color: "#34d399",
|
||||
check: (s) => s.totalDecoded >= 0 || s.crystals > 0,
|
||||
reward: { crystals: 5 },
|
||||
rewardText: "+5 晶体",
|
||||
},
|
||||
{
|
||||
id: "ach_first_decode",
|
||||
name: "谐振初鸣",
|
||||
desc: "成功解码第一颗记忆晶体",
|
||||
icon: "◈",
|
||||
color: "#fb7185",
|
||||
check: (s) => s.totalDecoded >= 1,
|
||||
reward: { insights: 3 },
|
||||
rewardText: "+3 洞见",
|
||||
},
|
||||
{
|
||||
id: "ach_decoded_10",
|
||||
name: "回响解码者",
|
||||
desc: "累计解码 10 颗晶体",
|
||||
icon: "❖",
|
||||
color: "#fbbf24",
|
||||
check: (s) => s.totalDecoded >= 10,
|
||||
reward: { insights: 15, crystalsPerSecPct: 5 },
|
||||
rewardText: "+15 洞见 · 产能 +5%",
|
||||
},
|
||||
{
|
||||
id: "ach_decoded_25",
|
||||
name: "记忆织匠",
|
||||
desc: "累计解码 25 颗晶体",
|
||||
icon: "✺",
|
||||
color: "#e879f9",
|
||||
check: (s) => s.totalDecoded >= 25,
|
||||
reward: { insights: 40, crystalsPerSecPct: 8 },
|
||||
rewardText: "+40 洞见 · 产能 +8%",
|
||||
},
|
||||
{
|
||||
id: "ach_decoded_50",
|
||||
name: "星核解密师",
|
||||
desc: "累计解码 50 颗晶体",
|
||||
icon: "✷",
|
||||
color: "#34d399",
|
||||
check: (s) => s.totalDecoded >= 50,
|
||||
reward: { insights: 100, crystalsPerSecPct: 12, insightPct: 10 },
|
||||
rewardText: "+100 洞见 · 产能 +12% · 洞见 +10%",
|
||||
},
|
||||
{
|
||||
id: "ach_tech_3",
|
||||
name: "初窥门径",
|
||||
desc: "解锁 3 项技术",
|
||||
icon: "⚙",
|
||||
color: "#fbbf24",
|
||||
check: (s) => Object.values(s.tech).filter((v) => v > 0).length >= 3,
|
||||
reward: { insights: 20 },
|
||||
rewardText: "+20 洞见",
|
||||
},
|
||||
{
|
||||
id: "ach_tech_all",
|
||||
name: "全谱精通",
|
||||
desc: "解锁全部 12 项技术",
|
||||
icon: "⬡",
|
||||
color: "#e879f9",
|
||||
check: (s) => Object.values(s.tech).filter((v) => v > 0).length >= 12,
|
||||
reward: { crystalsPerSecPct: 15, insightPct: 15 },
|
||||
rewardText: "产能 +15% · 洞见 +15%",
|
||||
},
|
||||
{
|
||||
id: "ach_frag_4",
|
||||
name: "残篇拾遗",
|
||||
desc: "拼凑 4 段记忆碎片",
|
||||
icon: "▤",
|
||||
color: "#fb7185",
|
||||
check: (s) => Object.values(s.fragments).filter(Boolean).length >= 4,
|
||||
reward: { contact: 10, insights: 25 },
|
||||
rewardText: "+10 接触 · +25 洞见",
|
||||
},
|
||||
{
|
||||
id: "ach_frag_all",
|
||||
name: "回响全谱",
|
||||
desc: "拼凑全部首纪元记忆碎片",
|
||||
icon: "▦",
|
||||
color: "#34d399",
|
||||
check: (s) => Object.values(s.fragments).filter(Boolean).length >= 8,
|
||||
reward: { contact: 25, crystalsPerSecPct: 10 },
|
||||
rewardText: "+25 接触 · 产能 +10%",
|
||||
},
|
||||
{
|
||||
id: "ach_exp_1",
|
||||
name: "初探遗迹",
|
||||
desc: "完成第一次遗迹探险",
|
||||
icon: "▲",
|
||||
color: "#fbbf24",
|
||||
check: (s) => s.totalExpeditions >= 1,
|
||||
reward: { insights: 10 },
|
||||
rewardText: "+10 洞见",
|
||||
},
|
||||
{
|
||||
id: "ach_exp_5",
|
||||
name: "遗迹猎手",
|
||||
desc: "累计出发 5 次探险",
|
||||
icon: "⬢",
|
||||
color: "#fb7185",
|
||||
check: (s) => s.totalExpeditions >= 5,
|
||||
reward: { insights: 30, crystalsPerSecPct: 5 },
|
||||
rewardText: "+30 洞见 · 产能 +5%",
|
||||
},
|
||||
{
|
||||
id: "ach_prestige_1",
|
||||
name: "初次接触",
|
||||
desc: "完成第一次飞升",
|
||||
icon: "✧",
|
||||
color: "#e879f9",
|
||||
check: (s) => s.ascensions >= 1,
|
||||
reward: { crystalsPerSecPct: 10, insightPct: 10 },
|
||||
rewardText: "产能 +10% · 洞见 +10%",
|
||||
},
|
||||
{
|
||||
id: "ach_prestige_3",
|
||||
name: "维度行者",
|
||||
desc: "累计飞升 3 次",
|
||||
icon: "✶",
|
||||
color: "#34d399",
|
||||
check: (s) => s.ascensions >= 3,
|
||||
reward: { crystalsPerSecPct: 20, insightPct: 20 },
|
||||
rewardText: "产能 +20% · 洞见 +20%",
|
||||
},
|
||||
{
|
||||
id: "ach_warehouse",
|
||||
name: "满仓时刻",
|
||||
desc: "晶体储量达到仓库上限",
|
||||
icon: "▣",
|
||||
color: "#fbbf24",
|
||||
check: (s) => s.crystals >= s.crystalCap,
|
||||
reward: { insights: 8 },
|
||||
rewardText: "+8 洞见",
|
||||
},
|
||||
];
|
||||
|
||||
/** 计算成就提供的永久加成(跨周目保留) */
|
||||
export function achievementBonuses(unlocked: Record<string, boolean>): {
|
||||
crystalsPerSecPct: number;
|
||||
insightPct: number;
|
||||
} {
|
||||
let crystalsPerSecPct = 0;
|
||||
let insightPct = 0;
|
||||
for (const a of ACHIEVEMENTS) {
|
||||
if (!unlocked[a.id]) continue;
|
||||
crystalsPerSecPct += a.reward.crystalsPerSecPct ?? 0;
|
||||
insightPct += a.reward.insightPct ?? 0;
|
||||
}
|
||||
return { crystalsPerSecPct, insightPct };
|
||||
}
|
||||
Reference in New Issue
Block a user