Files
echo-nexus/.next/dev/static/chunks/src_f0d25b4c._.js
T

1755 lines
97 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
"[project]/src/store/gameStore.ts [app-client] (ecmascript) <locals>", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"nextFragmentThreshold",
()=>nextFragmentThreshold,
"useGameStore",
()=>useGameStore
]);
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$build$2f$polyfills$2f$process$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = /*#__PURE__*/ __turbopack_context__.i("[project]/node_modules/next/dist/build/polyfills/process.js [app-client] (ecmascript)");
// 回响星核 / Echo Nexus — Zustand 游戏状态管理
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$zustand$2f$esm$2f$react$2e$mjs__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/zustand/esm/react.mjs [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$zustand$2f$esm$2f$middleware$2e$mjs__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/zustand/esm/middleware.mjs [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/config.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/engine.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/decode.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/expedition.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$achievements$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/achievements.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/starTide.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/constellation.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$chronicle$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/chronicle.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/beacon.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$offlineReport$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/offlineReport.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/attributes.ts [app-client] (ecmascript)");
"use client";
;
;
;
;
;
;
;
;
;
;
;
;
;
/** 全球星潮持续毫秒数(与 mini-service DURATION_SEC * 1000 一致) */ const GLOBAL_TIDE_DURATION_MS = 60_000;
/** 计算并写回产能字段 */ function syncStats(state) {
const s = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["recomputeStats"])(state);
return {
crystalsPerSec: s.crystalsPerSec,
crystalCap: s.crystalCap,
pulsePower: s.pulsePower,
offlineEff: s.offlineEff,
insightMult: s.insightMult,
contactRateMult: s.contactRateMult,
autoDecode: s.autoDecode,
decodeStepsBonus: s.decodeStepsBonus
};
}
/**
* 深空信标进度追踪(v0.5 → v0.8 → v0.8.2 扩展)。
* 在游戏关键动作(脉冲/解码/探险/BOSS/洞见)后调用,同时更新:
* 1. 日挑战进度(按今日挑战类型增量)
* 2. 限时挑战进度(按当前 4 小时时段挑战类型增量)
* 3. 周挑战进度(按本周挑战类型增量)
* 4. 信标链:日挑战刚完成时记录一次完成(含 grace 续命逻辑)
* 进度独立存储于 localStorage,不污染 GameState。
* @returns 四类状态变更供 UI 触发通知
*/ function trackBeacon(type, delta) {
const result = {
dailyJustCompleted: false,
timedJustCompleted: false,
weeklyJustCompleted: false,
newChainMilestones: []
};
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
;
try {
// ---- 日挑战 ----
const challenge = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateDailyChallenge"])();
if (challenge.type === type) {
const current = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadDailyProgress"])();
if (current.completedAt === null) {
const { justCompleted } = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["addBeaconProgress"])(current, challenge, delta);
result.dailyJustCompleted = justCompleted;
// 日挑战刚完成 → 更新信标链
if (justCompleted) {
const { newMilestones } = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["recordChainCompletion"])((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTodayKey"])());
result.newChainMilestones = newMilestones;
}
}
}
// ---- 限时挑战(v0.8.2----
const tChallenge = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateTimedChallenge"])();
if (tChallenge.type === type) {
const tCurrent = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadTimedProgress"])();
if (tCurrent.completedAt === null) {
const { justCompleted } = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["addTimedProgress"])(tCurrent, tChallenge, delta);
result.timedJustCompleted = justCompleted;
}
}
// ---- 周挑战 ----
const wChallenge = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateWeeklyChallenge"])();
if (wChallenge.type === type) {
const wCurrent = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadWeeklyProgress"])();
if (wCurrent.completedAt === null) {
const { justCompleted } = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["addWeeklyProgress"])(wCurrent, wChallenge, delta);
result.weeklyJustCompleted = justCompleted;
}
}
return result;
} catch {
return result;
}
}
/** 检查并解锁叙事碎片 */ function checkFragments(state) {
const unlocked = [];
for (const f of __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["FRAGMENTS"]){
if (!state.fragments[f.id] && state.totalDecoded >= f.threshold) {
state.fragments[f.id] = true;
unlocked.push(f.id);
}
}
return unlocked;
}
const useGameStore = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$zustand$2f$esm$2f$react$2e$mjs__$5b$app$2d$client$5d$__$28$ecmascript$29$__["create"])()((0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$zustand$2f$esm$2f$middleware$2e$mjs__$5b$app$2d$client$5d$__$28$ecmascript$29$__["persist"])((set, get)=>({
...(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialState"])(),
_lastAutoDecode: Date.now(),
_lastSpawn: Date.now(),
_combo: 0,
_lastPulse: 0,
_achievementQueue: [],
_tideEvents: [],
globalTide: null,
init: ()=>{
const s = get();
const now = Date.now();
// 兼容旧存档:若存在不可解的活跃谜题(旧算法生成),直接清掉
let activePuzzle = s.activePuzzle;
if (activePuzzle && !(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["isSolvable"])(activePuzzle)) {
// 把晶体放回队列,避免玩家卡死
const crystal = {
id: `c_${now}_rec`,
tier: activePuzzle.tier,
value: __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_VALUE"][activePuzzle.tier].crystals,
createdAt: now
};
activePuzzle = null;
set({
pendingCrystals: [
...s.pendingCrystals,
crystal
].slice(-__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_SPAWN"].maxPending)
});
}
// 兼容旧存档:补全 achievements / 星潮 / 星图 字段
const achievements = s.achievements ?? {};
const activeTide = s.activeTide ?? null;
const constellation = s.constellation ?? [];
const pendingPerkChoices = s.pendingPerkChoices ?? null;
// v0.4 编年史兼容:补全 chronicle / runStart / bossKills / starTidesEncountered
const migrated = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$chronicle$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["migrateChronicleFields"])(s);
// v0.7 角色属性兼容:补全 attributes / attributeProgress / pendingAttrPoints
const attrMigrated = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["migrateAttributes"])(s);
// 星图「能量共振」天赋 +1 能量上限
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(constellation);
const energyMax = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["INITIAL_STATE"].energyMax + cm.energyMaxBonus;
// lastTideEnd:0 表示从未触发过星潮(首次游玩),保留 0 让 firstDelay 生效
const lastTideEndRaw = s.lastTideEnd ?? 0;
// 若旧存档有已过期的星潮,清掉
const tide = activeTide && activeTide.endsAt > now ? activeTide : null;
// 首次进入:补发离线收益
const elapsed = Math.max(0, (now - s.lastTick) / 1000);
if (elapsed > 5) {
const cap = 8 * 3600;
const secs = Math.min(elapsed, cap);
const gain = s.crystalsPerSec * secs * s.offlineEff;
const crystalsBefore = s.crystals;
const crystalsAfter = Math.min(s.crystalCap, s.crystals + gain);
// 设置离线报告(v0.5.2):通知 UI 弹出"欢迎回来"对话框
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$offlineReport$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["setPendingOfflineReport"])({
elapsedSec: secs,
rawElapsedSec: elapsed,
gain: crystalsAfter - crystalsBefore,
rate: s.crystalsPerSec,
eff: s.offlineEff,
capped: elapsed > cap,
crystalsBefore,
crystalsAfter,
crystalCap: s.crystalCap
});
set({
crystals: crystalsAfter,
lastTick: now,
activePuzzle,
achievements,
activeTide: tide,
lastTideEnd: tide ? lastTideEndRaw : lastTideEndRaw,
constellation,
pendingPerkChoices,
energyMax,
chronicle: migrated.chronicle,
runStart: migrated.runStart,
bossKills: migrated.bossKills,
starTidesEncountered: migrated.starTidesEncountered,
attributes: attrMigrated.attributes,
attributeProgress: attrMigrated.attributeProgress,
pendingAttrPoints: attrMigrated.pendingAttrPoints,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements,
activeTide: tide,
constellation,
attributes: attrMigrated.attributes
})
});
} else {
set({
lastTick: now,
activePuzzle,
achievements,
activeTide: tide,
lastTideEnd: tide ? lastTideEndRaw : lastTideEndRaw,
constellation,
pendingPerkChoices,
energyMax,
chronicle: migrated.chronicle,
runStart: migrated.runStart,
bossKills: migrated.bossKills,
starTidesEncountered: migrated.starTidesEncountered,
attributes: attrMigrated.attributes,
attributeProgress: attrMigrated.attributeProgress,
pendingAttrPoints: attrMigrated.pendingAttrPoints,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements,
activeTide: tide,
constellation,
attributes: attrMigrated.attributes
})
});
}
},
loadOnline: ()=>{
const s = get();
set({
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: s.attributes
})
});
},
hardReset: ()=>{
set({
...(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialState"])(),
_lastAutoDecode: Date.now(),
_lastSpawn: Date.now(),
_combo: 0,
_lastPulse: 0,
_achievementQueue: [],
_tideEvents: [],
globalTide: null
});
},
tickTide: (now)=>{
const s = get();
// === P3 全球星潮优先:若全球星潮进行中,本地不触发新的星潮 ===
if (s.globalTide) {
// 1a) 检查全球星潮是否结束
if (now >= s.globalTide.endsAt) {
const endedType = s.globalTide.type;
let silenceCompensation = 0;
if (endedType === "silence") {
silenceCompensation = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeSilenceCompensation"])({
type: endedType,
startedAt: s.globalTide.startedAt,
endsAt: s.globalTide.endsAt,
id: s.globalTide.id
});
}
const event = {
ended: endedType,
isGlobal: true
};
if (silenceCompensation > 0) event.silenceCompensation = silenceCompensation;
set({
globalTide: null,
activeTide: null,
lastTideEnd: now,
insights: s.insights + silenceCompensation,
// 星潮结束后重算 stats
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: null,
constellation: s.constellation,
attributes: s.attributes
}),
_tideEvents: [
...s._tideEvents,
event
]
});
return event;
}
// 1b) 全球星潮仍在进行 — 不做本地处理(避免冲突)
return null;
}
const tide = s.activeTide;
// 星图「星潮引导」减少间隙
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(s.constellation ?? []);
// v0.7 灵感:星潮触发概率 +X%(缩短间隙)
const am = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAllBonuses"])(s.attributes ?? {});
const tideGapReduction = Math.min(0.3, am.tideTriggerBonus);
const gap = Math.max(15000, (__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["TIDE_CONFIG"].gap + cm.tideGapDeltaSec * 1000) * (1 - tideGapReduction));
// 2) 检查当前本地星潮是否结束
if (tide && now >= tide.endsAt) {
const endedType = tide.type;
// 寂静期补偿洞见
let silenceCompensation = 0;
if (tide.type === "silence") {
silenceCompensation = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeSilenceCompensation"])(tide);
}
const newInsights = s.insights + silenceCompensation;
const event = {
ended: endedType
};
if (silenceCompensation > 0) event.silenceCompensation = silenceCompensation;
set({
activeTide: null,
lastTideEnd: now,
insights: newInsights,
// 星潮结束后重算 stats(移除 contactRate/insight 修饰)
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: null,
constellation: s.constellation,
attributes: s.attributes
}),
_tideEvents: [
...s._tideEvents,
event
]
});
return event;
}
// 3) 检查是否该触发新星潮(间隙已过)
if (!tide) {
const since = now - s.lastTideEnd;
// 首次:以 createdAt 起算 firstDelay;之后以 lastTideEnd 起算 gap
const firstStart = s.lastTideEnd === 0 || s.lastTideEnd <= s.createdAt + 1000;
const need = firstStart ? __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["TIDE_CONFIG"].firstDelay : gap;
if (since >= need) {
const type = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["rollTide"])();
const newTide = {
id: `tide_${now}_${Math.random().toString(36).slice(2, 7)}`,
type,
startedAt: now,
endsAt: now + __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["TIDE_CONFIG"].duration
};
const event = {
started: type
};
// v0.4 编年史:累计遇到的星潮 ID(去重)
const tidesAll = s.starTidesEncountered ?? [];
const tideId = `tide_${type}`;
const newTidesAll = tidesAll.includes(tideId) ? tidesAll : [
...tidesAll,
tideId
];
set({
activeTide: newTide,
starTidesEncountered: newTidesAll,
// 星潮开始后重算 stats(应用 contactRate/insight 修饰)
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: newTide,
constellation: s.constellation,
attributes: s.attributes
}),
_tideEvents: [
...s._tideEvents,
event
]
});
return event;
}
}
return null;
},
consumeTideEvents: ()=>{
const s = get();
if (s._tideEvents.length === 0) return [];
const items = s._tideEvents;
set({
_tideEvents: []
});
return items;
},
// P3 socket 多人同步:服务端广播全球星潮时调用,强制覆盖本地冷却
triggerGlobalTide: (type)=>{
const s = get();
const now = Date.now();
const id = `global_tide_${now}_${Math.random().toString(36).slice(2, 7)}`;
const newTide = {
id,
type,
startedAt: now,
endsAt: now + GLOBAL_TIDE_DURATION_MS
};
const newGlobalTide = {
type,
startedAt: now,
endsAt: now + GLOBAL_TIDE_DURATION_MS,
id
};
// 编年史:累计遇到的星潮 ID(去重)
const tidesAll = s.starTidesEncountered ?? [];
const tideId = `tide_${type}`;
const newTidesAll = tidesAll.includes(tideId) ? tidesAll : [
...tidesAll,
tideId
];
const event = {
started: type,
isGlobal: true
};
set({
globalTide: newGlobalTide,
activeTide: newTide,
starTidesEncountered: newTidesAll,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: newTide,
constellation: s.constellation,
attributes: s.attributes
}),
_tideEvents: [
...s._tideEvents,
event
]
});
},
tick: (now)=>{
const s = get();
const dt = Math.max(0, (now - s.lastTick) / 1000);
if (dt <= 0) return;
// 星潮产能修饰(即时乘)
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const effCps = s.crystalsPerSec * tideMod.crystalsPerSecMult;
// 产能累加(仅闲置产能受仓库上限;探险奖励可超出)
const newCrystals = s.crystals >= s.crystalCap ? s.crystals // 已达/超上限,不再自动产出
: Math.min(s.crystalCap, s.crystals + effCps * dt);
// 自动产出待解码晶体(每 baseInterval 秒一颗,受技术/飞升略提速)
const bpBoost = 1 + s.blueprints.length * 0.03;
const spawnInterval = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_SPAWN"].baseIntervalSec / bpBoost * 1000;
let pending = s.pendingCrystals;
let lastSpawn = s._lastSpawn;
if (now - lastSpawn > spawnInterval && pending.length < __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_SPAWN"].maxPending) {
// 星图「晶体富集」提升 T2/T3 概率
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(s.constellation ?? []);
const tier = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["rollCrystalTierWithBonus"])(cm.t2t3BonusRate);
const crystal = {
id: `c_${now}_${Math.random().toString(36).slice(2, 7)}`,
tier,
value: __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_VALUE"][tier].crystals,
createdAt: now
};
pending = [
...pending,
crystal
];
lastSpawn = now;
}
// 能量恢复(探险系统,v0.8.1 动态间隔)
let energy = s.energy;
let lastEnergyTick = s.lastEnergyTick;
if (energy < s.energyMax) {
const intervalSec = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeEnergyRegenInterval"])(s);
const regen = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeEnergyRegen"])(lastEnergyTick, now, energy, s.energyMax, intervalSec);
energy = regen.energy;
lastEnergyTick = regen.lastTick;
} else {
lastEnergyTick = now;
}
set({
crystals: newCrystals,
lastTick: now,
pendingCrystals: pending,
_lastSpawn: lastSpawn,
energy,
lastEnergyTick
});
},
pulse: ()=>{
const s = get();
const now = Date.now();
// 连击
let combo = 1;
if (now - s._lastPulse < 1500) {
combo = Math.min(10, s._combo + 1);
}
const mult = 1 + (combo - 1) * 0.15;
// 星潮脉冲威力修饰
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
// v0.7 灵感:脉冲连击加成 +X%
const am = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAllBonuses"])(s.attributes ?? {});
const comboBonusMult = 1 + am.pulseComboBonus * Math.max(0, combo - 1);
const gain = s.pulsePower * mult * tideMod.pulsePowerMult * comboBonusMult;
set({
crystals: Math.min(s.crystalCap, s.crystals + gain),
_combo: combo,
_lastPulse: now
});
// 深空信标:脉冲任务进度 +1
trackBeacon("pulse", 1);
// v0.7 角色属性:连击 ≥3 给灵感经验
if (combo >= 3) {
const expGain = 1 + Math.floor(combo / 2); // 3 连击=2, 5 连击=3, 10 连击=6
// 内联经验获取(避免递归调用 set)
const prog = s.attributeProgress?.inspiration ?? {
exp: 0,
level: s.attributes?.inspiration ?? 0
};
const nextExp = prog.exp + expGain;
const lvlResult = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: nextExp,
level: s.attributes?.inspiration ?? 0
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const newAttributes = {
...s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])(),
inspiration: lvlResult.newProgress.level
};
const newProgress = {
...s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])(),
inspiration: lvlResult.newProgress
};
set({
attributes: newAttributes,
attributeProgress: newProgress,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
}
return {
gain,
combo
};
},
startDecode: (crystalId)=>{
const s = get();
const crystal = s.pendingCrystals.find((c)=>c.id === crystalId);
if (!crystal) return;
// v0.8.2 涌动星潮:解码目标序列 +2(更长谜题)
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const targetLenBonus = tideMod.targetLenBonus ?? 0;
const puzzle = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generatePuzzle"])(crystal.tier, undefined, targetLenBonus);
set({
activePuzzle: puzzle,
pendingCrystals: s.pendingCrystals.filter((c)=>c.id !== crystalId)
});
},
clickNode: (nodeId)=>{
const s = get();
if (!s.activePuzzle) return {
ok: false,
finished: false,
failReason: "无活跃谜题"
};
// 深拷贝谜题
const puzzle = JSON.parse(JSON.stringify(s.activePuzzle));
const res = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["tryClickNode"])(puzzle, nodeId);
if (res.ok) {
if (res.finished) {
// 结算奖励(星潮解码奖励修饰 + 星图「二周目经验」)
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(s.constellation ?? []);
const base = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["decodeRewards"])(puzzle.tier, s.insightMult, s.contactRateMult);
const finalMult = tideMod.decodeRewardMult * cm.decodeRewardMult;
const rewards = {
crystals: Math.round(base.crystals * finalMult),
insights: Math.round(base.insights * finalMult),
contact: +(base.contact * finalMult).toFixed(2)
};
const newTotal = s.totalDecoded + 1;
const newContact = Math.min(100, s.contact + rewards.contact);
const newInsights = s.insights + rewards.insights;
const newCrystals = s.crystals + rewards.crystals;
// 解锁碎片
const tentative = {
...s,
totalDecoded: newTotal,
fragments: {
...s.fragments
}
};
const unlocked = checkFragments(tentative);
// v0.7 角色属性:完成解码给智慧经验(tier 越高经验越多)
const wisdomExpGain = puzzle.tier * 2;
const curAttrs = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
const curProg = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
const progEntry = curProg.wisdom ?? {
exp: 0,
level: curAttrs.wisdom
};
const wisdomLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: progEntry.exp + wisdomExpGain,
level: curAttrs.wisdom
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const newAttributes = {
...curAttrs,
wisdom: wisdomLvl.newProgress.level
};
const newProgress = {
...curProg,
wisdom: wisdomLvl.newProgress
};
set({
activePuzzle: null,
crystals: newCrystals,
insights: newInsights,
contact: newContact,
totalDecoded: newTotal,
fragments: tentative.fragments,
attributes: newAttributes,
attributeProgress: newProgress,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
// 深空信标:解码 +1,洞见累计
trackBeacon("decode", 1);
trackBeacon("insight", rewards.insights);
return {
ok: true,
finished: true,
failReason: unlocked.join(",") || undefined
};
}
// 点击成功但未完成:检测当前局面是否仍可解
const solvable = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["isSolvable"])(puzzle);
set({
activePuzzle: puzzle
});
return {
ok: true,
finished: false,
solvable
};
}
return res;
},
undoStep: ()=>{
const s = get();
if (!s.activePuzzle || s.activePuzzle.path.length === 0) return;
const puzzle = JSON.parse(JSON.stringify(s.activePuzzle));
const lastId = puzzle.path.pop();
if (lastId !== undefined) {
const node = puzzle.grid.find((n)=>n.id === lastId);
if (node) node.used = false;
}
set({
activePuzzle: puzzle
});
},
retryPuzzle: ()=>{
const s = get();
if (!s.activePuzzle) return;
set({
activePuzzle: (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$decode$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["resetPuzzle"])(s.activePuzzle)
});
},
abandonPuzzle: ()=>{
const s = get();
if (!s.activePuzzle) return;
// 晶体放回队列末尾
const crystal = {
id: `c_${Date.now()}_ret`,
tier: s.activePuzzle.tier,
value: __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_VALUE"][s.activePuzzle.tier].crystals,
createdAt: Date.now()
};
set({
activePuzzle: null,
pendingCrystals: [
...s.pendingCrystals,
crystal
].slice(-__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CRYSTAL_SPAWN"].maxPending)
});
},
autoDecodeTick: ()=>{
const s = get();
if (!s.autoDecode) return;
const now = Date.now();
// 星图「自动校准」减少自动解码周期
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(s.constellation ?? []);
// v0.7 智慧:自动解码周期 -X%
const am = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAllBonuses"])(s.attributes ?? {});
const baseInterval = 12000 + cm.autoDecodeIntervalDeltaSec * 1000;
// v0.8.2 棱镜星潮:自动解码周期 ×0.7(-30%)
const tideModEarly = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const tideIntervalMult = tideModEarly.autoDecodeIntervalMult ?? 1;
const interval = Math.max(5000, baseInterval * am.autoDecodeIntervalMult * tideIntervalMult);
if (now - s._lastAutoDecode < interval) return;
// 找一颗 T1 晶体自动解码;v0.9 dec_5「全息解码核心」解锁后可自动解码 T2
let autoTier = 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 = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const base = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["decodeRewards"])(autoTier, s.insightMult, s.contactRateMult);
const finalMult = tideMod.decodeRewardMult * cm.decodeRewardMult;
const rewards = {
crystals: Math.round(base.crystals * finalMult),
insights: Math.round(base.insights * finalMult),
contact: +(base.contact * finalMult).toFixed(2)
};
const newTotal = s.totalDecoded + 1;
const tentative = {
...s,
totalDecoded: newTotal,
fragments: {
...s.fragments
}
};
checkFragments(tentative);
// v0.7 角色属性:自动解码给智慧经验(少量)
const curAttrs = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
const curProg = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
const progEntry = curProg.wisdom ?? {
exp: 0,
level: curAttrs.wisdom
};
const wisdomLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: progEntry.exp + 1,
level: curAttrs.wisdom
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const newAttributes = {
...curAttrs,
wisdom: wisdomLvl.newProgress.level
};
const newProgress = {
...curProg,
wisdom: wisdomLvl.newProgress
};
set({
pendingCrystals: s.pendingCrystals.filter((c)=>c.id !== crystal.id),
crystals: s.crystals + rewards.crystals,
insights: s.insights + rewards.insights,
contact: Math.min(100, s.contact + rewards.contact),
totalDecoded: newTotal,
fragments: tentative.fragments,
_lastAutoDecode: now,
attributes: newAttributes,
attributeProgress: newProgress,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
// 深空信标:自动解码也算进度
trackBeacon("decode", 1);
trackBeacon("insight", rewards.insights);
},
buyTech: (techId)=>{
const s = get();
const node = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["TECH_TREE"].find((t)=>t.id === techId);
if (!node) return false;
const cur = s.tech[techId] ?? 0;
if (cur >= 1) return false; // v0.1 每节点 1 级
if (s.insights < node.cost) return false;
const newTech = {
...s.tech,
[techId]: 1
};
set({
insights: s.insights - node.cost,
tech: newTech,
...syncStats({
tech: newTech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: s.attributes
})
});
return true;
},
// ============ 探险系统 ============
startExpedition: ()=>{
const s = get();
if (s.activeExpedition && !s.activeExpedition.finished) {
return {
ok: false,
reason: "已有进行中的探险"
};
}
if (s.energy < __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["EXPEDITION_CONFIG"].energyCost) {
return {
ok: false,
reason: "能量不足"
};
}
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
// v0.7 角色属性:探索力 +X% 探险力,勇气 +X 探险生命
const am = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAllBonuses"])(s.attributes ?? {});
const basePower = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeExpeditionPower"])(s) + tideMod.expeditionPowerBonus;
const baseHp = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeExpeditionHp"])(s) + tideMod.expeditionHpBonus;
const power = Math.round(basePower * am.expeditionPowerMult);
const hp = baseHp + am.expeditionHpBonus;
const seed = Math.floor(Math.random() * 1e9);
const expedition = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateExpedition"])(seed, power, hp);
set({
activeExpedition: expedition,
energy: s.energy - __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["EXPEDITION_CONFIG"].energyCost,
totalExpeditions: s.totalExpeditions + 1
});
return {
ok: true
};
},
resolveCurrentNode: ()=>{
const s = get();
if (!s.activeExpedition || s.activeExpedition.finished) return null;
// 深拷贝
const exp = JSON.parse(JSON.stringify(s.activeExpedition));
// v0.7 勇气:BOSS 战胜率 +X%(动态提高 RNG 阈值)
// v0.8.2 蚀相星潮:BOSS 胜率 +20%
const nodeBefore = exp.nodes[exp.currentNode];
const isBossNode = nodeBefore?.type === "boss";
const am = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAllBonuses"])(s.attributes ?? {});
const tideMod = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$starTide$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getTideModifiers"])(s.activeTide);
const tideBossBonus = tideMod.bossWinRateBonus ?? 0;
const result = isBossNode ? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["resolveNode"])(exp, ()=>{
// 单次 rng() 调用:B% 概率返回 0(必胜),其余情况返回 r-B(保持均匀分布)
const r = Math.random();
const b = Math.min(0.95, am.bossWinRateBonus + tideBossBonus);
return r < b ? 0 : Math.min(1, r - b);
}) : (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$expedition$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["resolveNode"])(exp);
// 累计奖励
if (result.crystals) exp.rewards.crystals += result.crystals;
if (result.insights) exp.rewards.insights += result.insights;
if (result.contact) exp.rewards.contact += result.contact;
if (result.fragments) exp.rewards.fragments.push(...result.fragments);
if (result.hpDelta) exp.hp = Math.max(0, Math.min(exp.maxHp, exp.hp + result.hpDelta));
// 实时入账(玩家立即获得)
const newCrystals = s.crystals + (result.crystals || 0);
const newInsights = s.insights + (result.insights || 0);
const newContact = Math.min(100, s.contact + (result.contact || 0));
// 碎片解锁
const newFragments = {
...s.fragments
};
if (result.fragments) {
for (const fid of result.fragments)newFragments[fid] = true;
}
// 日志
const logEntry = {
expeditionId: exp.id,
nodeType: nodeBefore?.type || "combat",
result: result.log,
rewards: [
result.crystals ? `+${result.crystals}晶体` : "",
result.insights ? `+${result.insights}洞见` : "",
result.contact ? `+${result.contact.toFixed(1)}接触` : "",
result.hpDelta ? `${result.hpDelta > 0 ? "+" : ""}${result.hpDelta}生命` : ""
].filter(Boolean).join(" "),
timestamp: Date.now()
};
const newLog = [
logEntry,
...s.expeditionLog
].slice(0, 30);
// v0.10 工单 #2 修复:探险结束时立即清空 activeExpedition,避免 finished 状态卡死
// 保留 lastExpeditionSummary 用于入口展示「上次结果」
if (result.ended) {
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;
if (result.ended && result.endReason === "victory" && nodeBefore?.type === "boss") {
bossKills = bossKills + 1;
bossKilledThisNode = true;
}
// v0.7 角色属性:战斗胜利给勇气+探索力经验;BOSS 额外奖励
let newAttributes = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
let newProgress = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
let statsNeedResync = false;
// 战斗类节点(combat/boss)且胜利:勇气 + 探索力经验
const isCombatVictory = (nodeBefore?.type === "combat" || nodeBefore?.type === "boss") && !result.ended; // 中途战斗胜利(未结束探险)
const isExpeditionVictory = result.ended && result.endReason === "victory";
if (isCombatVictory || bossKilledThisNode || isExpeditionVictory) {
const courageGain = bossKilledThisNode ? 8 : 2;
const explorationGain = bossKilledThisNode ? 6 : isExpeditionVictory ? 4 : 1;
const courageLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: newProgress.courage.exp + courageGain,
level: newAttributes.courage
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const explLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: newProgress.exploration.exp + explorationGain,
level: newAttributes.exploration
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
newAttributes = {
...newAttributes,
courage: courageLvl.newProgress.level,
exploration: explLvl.newProgress.level
};
newProgress = {
...newProgress,
courage: courageLvl.newProgress,
exploration: explLvl.newProgress
};
statsNeedResync = true;
}
set({
// v0.10 工单 #2 修复:探险结束立即清空 activeExpedition,让玩家无障碍再出发
activeExpedition: result.ended ? null : exp,
crystals: newCrystals,
insights: newInsights,
contact: newContact,
fragments: newFragments,
expeditionLog: newLog,
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
}) : {}
});
// 深空信标:探险完成(胜负都计)+ 洞见累计 + BOSS 击破
if (result.ended) {
trackBeacon("expedition", 1);
if (bossKilledThisNode) {
trackBeacon("boss", 1);
}
}
if (result.insights) trackBeacon("insight", result.insights);
return result;
},
advanceNode: ()=>{
const s = get();
if (!s.activeExpedition || s.activeExpedition.finished) return;
const exp = JSON.parse(JSON.stringify(s.activeExpedition));
const node = exp.nodes[exp.currentNode];
if (!node || !node.cleared) return; // 当前节点未结算不能前进
if (exp.currentNode >= exp.nodes.length - 1) return;
exp.currentNode++;
set({
activeExpedition: exp
});
},
abortExpedition: ()=>{
const s = get();
if (!s.activeExpedition) return;
const exp = JSON.parse(JSON.stringify(s.activeExpedition));
const logEntry = {
expeditionId: exp.id,
nodeType: "rest",
result: "探险队主动撤退,保留已获奖励。",
rewards: "",
timestamp: Date.now()
};
// v0.9 工单修复:记录上次探险摘要,用于入口展示
const summary = {
result: "abort",
rewards: {
...exp.rewards
},
nodeCount: exp.nodes.length,
finishedAt: Date.now()
};
set({
// v0.10 工单 #2 修复:撤退立即清空 activeExpedition,避免 finished 卡死
activeExpedition: null,
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 < __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CONTACT"].prestigeMin) return null;
const newBp = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["computeNewBlueprints"])(s);
const next = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["performPrestige"])(s);
// 星图「能量共振」提升上限
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(next.constellation ?? []);
const energyMax = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["INITIAL_STATE"].energyMax + cm.energyMaxBonus;
set({
...next,
energyMax,
...syncStats({
tech: next.tech,
blueprints: next.blueprints,
achievements: next.achievements,
activeTide: next.activeTide,
constellation: next.constellation,
attributes: next.attributes
}),
_lastAutoDecode: Date.now(),
_lastSpawn: Date.now(),
_combo: 0,
_lastPulse: 0,
_tideEvents: []
});
return {
newBp
};
},
chooseConstellationPerk: (perkId)=>{
const s = get();
if (!s.pendingPerkChoices || !s.pendingPerkChoices.includes(perkId)) return false;
const perk = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getPerk"])(perkId);
if (!perk) return false;
if (s.constellation?.includes(perkId)) return false;
const newConstellation = [
...s.constellation ?? [],
perkId
];
const cm = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["constellationBonuses"])(newConstellation);
const energyMax = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["INITIAL_STATE"].energyMax + cm.energyMaxBonus;
// v0.4 编年史:回填最近一条 entry 的 perksThisAscension
const chronicle = s.chronicle ?? [];
let newChronicle = chronicle;
if (chronicle.length > 0) {
const lastEntry = chronicle[chronicle.length - 1];
const updatedLast = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$chronicle$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["withPerks"])(lastEntry, [
perkId
]);
newChronicle = [
...chronicle.slice(0, -1),
updatedLast
];
}
set({
constellation: newConstellation,
pendingPerkChoices: null,
energyMax,
chronicle: newChronicle,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: newConstellation,
attributes: s.attributes
})
});
return true;
},
rerollPerkChoices: ()=>{
const s = get();
if (!s.pendingPerkChoices) return;
const choices = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$constellation$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["rollPerkChoices"])(s.constellation ?? []);
if (choices.length > 0) set({
pendingPerkChoices: choices
});
},
checkAchievements: ()=>{
const s = get();
const newlyUnlocked = [];
const updated = {
...s.achievements
};
let crystals = s.crystals;
let insights = s.insights;
let contact = s.contact;
let statsDirty = false;
for (const a of __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$achievements$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ACHIEVEMENTS"]){
if (updated[a.id]) continue;
if (a.check(s)) {
updated[a.id] = true;
newlyUnlocked.push(a);
// 发放即时奖励
if (a.reward.crystals) crystals += a.reward.crystals;
if (a.reward.insights) insights += a.reward.insights;
if (a.reward.contact) contact = Math.min(100, contact + a.reward.contact);
if (a.reward.crystalsPerSecPct || a.reward.insightPct) statsDirty = true;
}
}
if (newlyUnlocked.length === 0) return [];
set({
achievements: updated,
crystals,
insights,
contact,
...statsDirty ? syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: updated,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: s.attributes
}) : {},
_achievementQueue: [
...s._achievementQueue,
...newlyUnlocked
]
});
return newlyUnlocked;
},
consumeAchievementQueue: ()=>{
const s = get();
if (s._achievementQueue.length === 0) return [];
const items = s._achievementQueue;
set({
_achievementQueue: []
});
return items;
},
canPrestige: ()=>get().contact >= __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["CONTACT"].prestigeMin,
toggleTheme: ()=>set({
theme: get().theme === "dark" ? "light" : "dark"
}),
toggleSound: ()=>set({
soundOn: !get().soundOn
}),
// 深空信标:发放每日挑战奖励(v0.5)
grantBeaconReward: (insights, contact)=>{
const s = get();
set({
insights: s.insights + Math.round(insights),
contact: Math.min(100, s.contact + contact)
});
},
// 深空信标:领取周挑战奖励(v0.8)
claimWeeklyBeacon: ()=>{
const challenge = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateWeeklyChallenge"])();
const progress = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadWeeklyProgress"])();
const res = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["claimWeeklyReward"])(challenge, progress);
if (res.rewardInsight > 0 || res.rewardContact > 0) {
const s = get();
set({
insights: s.insights + Math.round(res.rewardInsight),
contact: Math.min(100, s.contact + res.rewardContact)
});
// 异步提交到云排行榜(失败静默,不影响本地)
if (res.entry) {
void (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["submitCloudScore"])(res.entry);
}
}
return {
rewardInsight: res.rewardInsight,
rewardContact: res.rewardContact,
score: res.score
};
},
// 深空信标:领取限时挑战奖励(v0.8.2)
claimTimedBeacon: ()=>{
const challenge = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["generateTimedChallenge"])();
const progress = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadTimedProgress"])();
const res = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["claimTimedReward"])(challenge, progress);
if (res.rewardInsight > 0 || res.rewardContact > 0) {
const s = get();
set({
insights: s.insights + Math.round(res.rewardInsight),
contact: Math.min(100, s.contact + res.rewardContact)
});
// 异步提交到云排行榜(失败静默,不影响本地)
if (res.entry) {
void (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["submitCloudScore"])(res.entry);
}
}
return {
rewardInsight: res.rewardInsight,
rewardContact: res.rewardContact,
score: res.score
};
},
// 深空信标:领取信标链里程碑奖励(v0.8)
claimChainReward: (milestone)=>{
// loadChainState 仅用于前置校验,真正的状态修改由 claimChainMilestone 完成
const pre = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["loadChainState"])();
if (pre.currentStreak < milestone || pre.milestonesClaimed.includes(milestone)) {
return {
rewardInsight: 0,
rewardContact: 0,
label: "",
ok: false
};
}
const res = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$beacon$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["claimChainMilestone"])(milestone);
if (res.rewardInsight > 0 || res.rewardContact > 0) {
const s = get();
set({
insights: s.insights + Math.round(res.rewardInsight),
contact: Math.min(100, s.contact + res.rewardContact)
});
}
return {
rewardInsight: res.rewardInsight,
rewardContact: res.rewardContact,
label: res.label,
ok: res.rewardInsight > 0 || res.rewardContact > 0
};
},
// 深空巡航:发放实时玩法奖励(v0.6)
grantCruiseReward: (rewards)=>{
const s = get();
const addCrystals = rewards.crystals ?? 0;
const addInsights = rewards.insights ?? 0;
const addContact = rewards.contact ?? 0;
// v0.7 角色属性:巡航通关给探索力+勇气经验(按晶体奖励量缩放)
const totalReward = addCrystals + addInsights * 10 + addContact * 10;
const expBase = Math.max(2, Math.floor(totalReward / 30));
const curAttrs = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
const curProg = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
const explLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: curProg.exploration.exp + expBase,
level: curAttrs.exploration
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const courageLvl = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: curProg.courage.exp + Math.floor(expBase * 0.6),
level: curAttrs.courage
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const newAttributes = {
...curAttrs,
exploration: explLvl.newProgress.level,
courage: courageLvl.newProgress.level
};
const newProgress = {
...curProg,
exploration: explLvl.newProgress,
courage: courageLvl.newProgress
};
set({
crystals: Math.min(s.crystalCap, s.crystals + addCrystals),
insights: s.insights + Math.round(addInsights),
contact: Math.min(100, s.contact + addContact),
attributes: newAttributes,
attributeProgress: newProgress,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
},
// ============ 角色属性系统(v0.7 P1 ============
allocateAttribute: (attr, points = 1)=>{
const s = get();
const cur = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
const curVal = cur[attr] ?? 0;
if (curVal >= __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]) {
return {
ok: false,
leveledUp: 0
};
}
if ((s.pendingAttrPoints ?? 0) < points) {
return {
ok: false,
leveledUp: 0
};
}
const alloc = Math.min(points, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"] - curVal, s.pendingAttrPoints);
const newVal = curVal + alloc;
const newAttributes = {
...cur,
[attr]: newVal
};
// 同步经验进度 level 字段(保持一致)
const curProg = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
const oldProg = curProg[attr] ?? {
exp: 0,
level: curVal
};
const newProgress = {
...curProg,
[attr]: {
exp: oldProg.exp,
level: newVal
}
};
set({
attributes: newAttributes,
attributeProgress: newProgress,
pendingAttrPoints: s.pendingAttrPoints - alloc,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
return {
ok: true,
leveledUp: alloc
};
},
gainAttributeExp: (attr, amount)=>{
const s = get();
const cur = s.attributes ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributes"])();
const curProg = s.attributeProgress ?? (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialAttributeProgress"])();
const progEntry = curProg[attr] ?? {
exp: 0,
level: cur[attr]
};
const result = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["levelUpCheck"])({
exp: progEntry.exp + amount,
level: cur[attr]
}, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$attributes$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["ATTRIBUTE_HARD_CAP"]);
const newAttributes = {
...cur,
[attr]: result.newProgress.level
};
const newProgress = {
...curProg,
[attr]: result.newProgress
};
set({
attributes: newAttributes,
attributeProgress: newProgress,
...syncStats({
tech: s.tech,
blueprints: s.blueprints,
achievements: s.achievements,
activeTide: s.activeTide,
constellation: s.constellation,
attributes: newAttributes
})
});
return {
leveledUp: result.levelsGained,
newLevel: result.newProgress.level
};
}
}), {
name: "echo-nexus-save-v2",
version: 2,
storage: (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$zustand$2f$esm$2f$middleware$2e$mjs__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createJSONStorage"])(()=>localStorage),
// v0.12 存档迁移:旧存档(v0/v1key=echo-nexus-save-v1)补全新增字段,防崩溃
migrate: (persistedState, version)=>{
void version; // v0/v1 统一走合并迁移,无需分支
const s = persistedState || {};
const base = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$engine$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["createInitialState"])();
// 合并:以当前 INITIAL_STATE 为基底,旧存档字段覆盖,缺失字段用默认值
const migrated = {
...base,
...s,
// v0.8+ 新增字段兜底
bossKills: s.bossKills ?? 0,
starTidesEncountered: s.starTidesEncountered ?? [],
chronicle: s.chronicle ?? [],
runStart: s.runStart ?? Date.now(),
// v0.8.2 attributes 系统
attributes: s.attributes ?? base.attributes,
attributeProgress: s.attributeProgress ?? base.attributeProgress,
pendingAttrPoints: s.pendingAttrPoints ?? 0,
// v0.10 探险系统字段兜底
activeExpedition: s.activeExpedition ?? null,
totalExpeditions: s.totalExpeditions ?? 0,
lastExpeditionSummary: s.lastExpeditionSummary ?? null,
// 星图兜底
constellation: s.constellation ?? [],
pendingPerkChoices: s.pendingPerkChoices ?? [],
// 技术树/碎片/成就兜底(防旧存档为 undefined)
tech: s.tech ?? {},
fragments: s.fragments ?? {},
achievements: s.achievements ?? {}
};
// 丢弃不可解的活跃谜题(旧算法生成)
if (migrated.activePuzzle && typeof migrated.activePuzzle === "object") {
const p = migrated.activePuzzle;
if (!p.nodes || !Array.isArray(p.nodes) || p.nodes.length === 0) {
migrated.activePuzzle = null;
}
}
return migrated;
},
// 不持久化临时字段
partialize: (s)=>{
const { _lastAutoDecode, _lastSpawn, _combo, _lastPulse, _achievementQueue, _tideEvents, globalTide, ...rest } = s;
void _lastAutoDecode;
void _lastSpawn;
void _combo;
void _lastPulse;
void _achievementQueue;
void _tideEvents;
void globalTide;
return rest;
}
}));
function nextFragmentThreshold(totalDecoded) {
for (const f of __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$config$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["FRAGMENTS"]){
if (totalDecoded < f.threshold) return f.threshold;
}
return null;
}
;
// 开发期调试:暴露 store 到 window,便于 QA 测试
if ("TURBOPACK compile-time truthy", 1) {
window.__gameStore = useGameStore;
}
if (typeof globalThis.$RefreshHelpers$ === 'object' && globalThis.$RefreshHelpers !== null) {
__turbopack_context__.k.registerExports(__turbopack_context__.m, globalThis.$RefreshHelpers$);
}
}),
"[project]/src/hooks/useAudio.ts [app-client] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"sfx",
()=>sfx,
"useAudioSync",
()=>useAudioSync
]);
// 回响星核 / Echo Nexus — 音频 React 适配
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/next/dist/compiled/react/index.js [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$audio$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/game/audio.ts [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/src/store/gameStore.ts [app-client] (ecmascript) <locals>");
var _s = __turbopack_context__.k.signature();
"use client";
;
;
;
function useAudioSync() {
_s();
const soundOn = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useAudioSync.useGameStore[soundOn]": (s)=>s.soundOn
}["useAudioSync.useGameStore[soundOn]"]);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useEffect"])({
"useAudioSync.useEffect": ()=>{
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$audio$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAudio"])().setEnabled(soundOn);
}
}["useAudioSync.useEffect"], [
soundOn
]);
}
_s(useAudioSync, "ZDNYY6yOyIKdqEM3NI9qVh7+PYk=", false, function() {
return [
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"]
];
});
function sfx(name, opts) {
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$game$2f$audio$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__["getAudio"])().play(name, opts);
}
if (typeof globalThis.$RefreshHelpers$ === 'object' && globalThis.$RefreshHelpers !== null) {
__turbopack_context__.k.registerExports(__turbopack_context__.m, globalThis.$RefreshHelpers$);
}
}),
"[project]/src/hooks/useGameLoop.ts [app-client] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"useGameLoop",
()=>useGameLoop
]);
// 回响星核 / Echo Nexus — 主循环 hook
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/next/dist/compiled/react/index.js [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/src/store/gameStore.ts [app-client] (ecmascript) <locals>");
var _s = __turbopack_context__.k.signature();
"use client";
;
;
function useGameLoop() {
_s();
const tick = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGameLoop.useGameStore[tick]": (s)=>s.tick
}["useGameLoop.useGameStore[tick]"]);
const autoDecodeTick = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGameLoop.useGameStore[autoDecodeTick]": (s)=>s.autoDecodeTick
}["useGameLoop.useGameStore[autoDecodeTick]"]);
const tickTide = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGameLoop.useGameStore[tickTide]": (s)=>s.tickTide
}["useGameLoop.useGameStore[tickTide]"]);
const checkAchievements = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGameLoop.useGameStore[checkAchievements]": (s)=>s.checkAchievements
}["useGameLoop.useGameStore[checkAchievements]"]);
const init = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGameLoop.useGameStore[init]": (s)=>s.init
}["useGameLoop.useGameStore[init]"]);
const inited = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useRef"])(false);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useEffect"])({
"useGameLoop.useEffect": ()=>{
// 等待 persist rehydrate 完成后再 initv0.5.2 修复离线收益不弹窗 BUG)
// Zustand persist 即使用同步 localStoragerehydrate 也是异步(Promise.resolve 包装)
const doInit = {
"useGameLoop.useEffect.doInit": ()=>{
if (!inited.current) {
init();
inited.current = true;
}
}
}["useGameLoop.useEffect.doInit"];
let unsub = null;
let fallback = null;
if (__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"].persist.hasHydrated()) {
doInit();
} else {
unsub = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"].persist.onFinishHydration(doInit);
// 兜底:500ms 后强制 init(防止 hydrated 状态异常)
fallback = setTimeout(doInit, 500);
}
let counter = 0;
const id = setInterval({
"useGameLoop.useEffect.id": ()=>{
const now = Date.now();
tick(now);
autoDecodeTick();
// 星潮检测每 tick 都查(结束/触发判定需及时)
tickTide(now);
counter++;
// 成就检测每秒执行一次(降低开销)
if (counter % 4 === 0) {
checkAchievements();
}
}
}["useGameLoop.useEffect.id"], 250);
return ({
"useGameLoop.useEffect": ()=>{
clearInterval(id);
if (unsub) unsub();
if (fallback) clearTimeout(fallback);
}
})["useGameLoop.useEffect"];
}
}["useGameLoop.useEffect"], [
tick,
autoDecodeTick,
tickTide,
checkAchievements,
init
]);
// 页面可见性:切回时补 tick + 星潮 + 成就
(0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useEffect"])({
"useGameLoop.useEffect": ()=>{
const onVis = {
"useGameLoop.useEffect.onVis": ()=>{
if (document.visibilityState === "visible") {
tick(Date.now());
tickTide(Date.now());
checkAchievements();
}
}
}["useGameLoop.useEffect.onVis"];
document.addEventListener("visibilitychange", onVis);
return ({
"useGameLoop.useEffect": ()=>document.removeEventListener("visibilitychange", onVis)
})["useGameLoop.useEffect"];
}
}["useGameLoop.useEffect"], [
tick,
tickTide,
checkAchievements
]);
}
_s(useGameLoop, "tLDT5k02T5AHn62KJSomBcU8OZw=", false, function() {
return [
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"],
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"],
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"],
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"],
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"]
];
});
if (typeof globalThis.$RefreshHelpers$ === 'object' && globalThis.$RefreshHelpers !== null) {
__turbopack_context__.k.registerExports(__turbopack_context__.m, globalThis.$RefreshHelpers$);
}
}),
"[project]/src/hooks/useGlobalTide.ts [app-client] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"adminTriggerGlobalTide",
()=>adminTriggerGlobalTide,
"useGlobalTide",
()=>useGlobalTide
]);
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$build$2f$polyfills$2f$process$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = /*#__PURE__*/ __turbopack_context__.i("[project]/node_modules/next/dist/build/polyfills/process.js [app-client] (ecmascript)");
// 回响星核 / Echo Nexus — 全球星潮同步 hookP3 / 工单 #10-c
//
// 连接 star-tide-service(端口 3031),监听全球星潮事件:
// - `tide-state` 连接时立即推送当前状态
// - `global-tide` 全球星潮开始 → 调用 gameStore.triggerGlobalTide(type)
// - `tide-ended` 全球星潮结束(本地 tickTide 也会在 60s 后自动收尾,这是双保险)
//
// 网关:socket.io 必须用 `io("/?XTransformPort=3031")`path 默认 "/"
// 离线容错:socket.io 默认自动重连;断连期间本地 tickTide 继续工作
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/next/dist/compiled/react/index.js [app-client] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$socket$2e$io$2d$client$2f$build$2f$esm$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/node_modules/socket.io-client/build/esm/index.js [app-client] (ecmascript) <locals>");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/src/store/gameStore.ts [app-client] (ecmascript) <locals>");
var _s = __turbopack_context__.k.signature();
"use client";
;
;
;
/** 单例 socket —— 多次挂载 hook 共享同一连接 */ let sharedSocket = null;
let connectRefCount = 0;
function getSharedSocket() {
if (!sharedSocket) {
sharedSocket = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$socket$2e$io$2d$client$2f$build$2f$esm$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["io"])("/?XTransformPort=3031", {
transports: [
"websocket",
"polling"
],
reconnection: true,
reconnectionAttempts: Infinity,
reconnectionDelay: 1000,
reconnectionDelayMax: 10_000,
timeout: 10_000
});
}
return sharedSocket;
}
function useGlobalTide() {
_s();
const triggerGlobalTide = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"])({
"useGlobalTide.useGameStore[triggerGlobalTide]": (s)=>s.triggerGlobalTide
}["useGlobalTide.useGameStore[triggerGlobalTide]"]);
// 用 ref 缓存最新 action,避免 socket 监听器依赖变化导致重绑
const triggerRef = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useRef"])(triggerGlobalTide);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useEffect"])({
"useGlobalTide.useEffect": ()=>{
triggerRef.current = triggerGlobalTide;
}
}["useGlobalTide.useEffect"], [
triggerGlobalTide
]);
// 记录已处理过的 global-tide 事件 id(避免重连时重复触发)
const processedIds = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useRef"])(new Set());
(0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2f$dist$2f$compiled$2f$react$2f$index$2e$js__$5b$app$2d$client$5d$__$28$ecmascript$29$__["useEffect"])({
"useGlobalTide.useEffect": ()=>{
const sock = getSharedSocket();
connectRefCount += 1;
const onGlobalTide = {
"useGlobalTide.useEffect.onGlobalTide": (tide)=>{
// 用 startedAt+type 作为去重 key(同一道全球星潮可能在重连后被重复推送)
const key = `${tide.startedAt}_${tide.type}`;
if (processedIds.current.has(key)) return;
processedIds.current.add(key);
// 限制集合大小
if (processedIds.current.size > 32) {
const first = processedIds.current.values().next().value;
if (first) processedIds.current.delete(first);
}
// 防御:如果服务端推送的星潮已经过期(例如重连时拿到的旧状态),不触发
if (tide.endsAt <= Date.now()) return;
triggerRef.current(tide.type);
}
}["useGlobalTide.useEffect.onGlobalTide"];
const onTideState = {
"useGlobalTide.useEffect.onTideState": (state)=>{
// 连接/重连时若服务端正有进行中的全球星潮,且本地还没触发过,补触发
if (state.current && state.current.endsAt > Date.now()) {
const key = `${state.current.startedAt}_${state.current.type}`;
if (!processedIds.current.has(key)) {
processedIds.current.add(key);
triggerRef.current(state.current.type);
}
}
}
}["useGlobalTide.useEffect.onTideState"];
sock.on("global-tide", onGlobalTide);
sock.on("tide-state", onTideState);
// 主动查询一次当前状态(保险,防止错过连接时的初始推送)
sock.emit("get-next-tide");
return ({
"useGlobalTide.useEffect": ()=>{
sock.off("global-tide", onGlobalTide);
sock.off("tide-state", onTideState);
connectRefCount -= 1;
// 最后一个使用者退出时才真正断开
if (connectRefCount <= 0 && sharedSocket) {
sharedSocket.disconnect();
sharedSocket = null;
connectRefCount = 0;
}
}
})["useGlobalTide.useEffect"];
}
}["useGlobalTide.useEffect"], []);
}
_s(useGlobalTide, "06te8jY+CFt5X79ydMcCUfRcDD8=", false, function() {
return [
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$store$2f$gameStore$2e$ts__$5b$app$2d$client$5d$__$28$ecmascript$29$__$3c$locals$3e$__["useGameStore"]
];
});
function adminTriggerGlobalTide() {
if (!sharedSocket) {
// 还没连接则先建立
getSharedSocket();
}
sharedSocket?.emit("admin-trigger-tide");
}
if ("TURBOPACK compile-time truthy", 1) {
window.__adminTriggerGlobalTide = adminTriggerGlobalTide;
}
if (typeof globalThis.$RefreshHelpers$ === 'object' && globalThis.$RefreshHelpers !== null) {
__turbopack_context__.k.registerExports(__turbopack_context__.m, globalThis.$RefreshHelpers$);
}
}),
]);
//# sourceMappingURL=src_f0d25b4c._.js.map