1755 lines
97 KiB
JavaScript
1755 lines
97 KiB
JavaScript
(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/v1,key=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 完成后再 init(v0.5.2 修复离线收益不弹窗 BUG)
|
||
// Zustand persist 即使用同步 localStorage,rehydrate 也是异步(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 — 全球星潮同步 hook(P3 / 工单 #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
|