MatchMaster/assets/Script/module/Config/BattlePassModel.ts

79 lines
4.4 KiB
TypeScript

/** 战令配置计算,不处理金币、登录或其他模块的同步。 */
export const PASS_PERIOD = 30 * 24 * 60 * 60 * 1000;
export const PASS_PRODUCTS = { battlepass: 1800, battlepass_30: 3000, battlepass_12: 1200 };
const OLD_TOKENS = [1, 2, 2, 2, 2, 2, 2, 2, 2, ...Array(21).fill(4)];
export function passTier(stage: any): number { return stage && stage.tier === 30 ? 30 : stage && stage.activate ? 18 : 0; }
export function legacyExperience(stage: any): number {
const level = Math.max(1, Math.min(30, Number(stage.progressLevel) || 1));
return OLD_TOKENS.slice(0, level - 1).reduce((n, cost) => n + cost, 0) + Math.min(OLD_TOKENS[level - 1], Number(stage.progress) || 0);
}
export function normalizePass(stage: any, rows: any[]): any {
if (!stage || !Number(stage.time)) return stage;
stage = { ...stage, free: [...(stage.free || [])], passCheck: [...(stage.passCheck || [])] };
if (!Number.isFinite(stage.experience)) {
const level = Math.max(1, Math.min(30, Number(stage.progressLevel) || 1));
stage.experience = rows.slice(0, level - 1).reduce((n, row) => n + Number(row.token), 0)
+ Math.min(Number(rows[level - 1].token), Number(stage.progress) || 0);
}
let remaining = Math.max(0, stage.experience), unlocked = 0;
for (const row of rows) {
if (remaining < Number(row.token)) break;
remaining -= Number(row.token);
unlocked++;
}
stage.unlocked = unlocked;
// 保持旧界面“当前档 + 当前档经验”的表示,完成一档时显示满格。
const complete = remaining === 0 || unlocked === rows.length;
stage.progressLevel = Math.max(1, Math.min(rows.length, unlocked + (complete ? 0 : 1)));
stage.progress = complete ? Number(rows[stage.progressLevel - 1].token) : remaining;
for (const lane of ['free', 'passCheck']) for (let i = 0; i < unlocked; i++) {
if (stage[lane][i] !== 0) stage[lane][i] = 1;
}
return stage;
}
export function mergePassInfo(raw: any, extra: any, rows: any[], oldRows: any[] = rows): any {
if (!raw) return raw;
for (const key of [1, 2]) {
const old = raw[key];
if (!old || !Number(old.time)) continue;
const saved = extra && extra[String(old.time)];
if (saved) {
raw[key] = { ...saved, activate: !!old.activate || !!saved.activate,
experience: saved.experience + Math.max(0, legacyExperience(old) - (saved.legacyExperience || 0)) };
for (const lane of ['free', 'passCheck']) {
raw[key][lane] = [...(saved[lane] || [])];
for (let i = 0; i < Math.min(30, (old[lane] || []).length); i++) if (old[lane][i] === 0) raw[key][lane][i] = 0;
}
}
raw[key] = normalizePass(raw[key], key === 1 ? oldRows : rows);
}
return raw;
}
export function legacyPassInfo(pass: any, rows: any[], oldRows: any[] = rows): any {
const legacy: any = {};
for (const key of [1, 2]) {
const stage = pass[key];
if (!stage || !Number(stage.time)) { legacy[key] = stage; continue; }
const level = Math.max(1, Math.min(30, stage.progressLevel));
const row = (key === 1 ? oldRows : rows)[stage.progressLevel - 1];
const progress = stage.unlocked >= level ? OLD_TOKENS[level - 1] : Math.min(OLD_TOKENS[level - 1], Math.floor(stage.progress * OLD_TOKENS[level - 1] / Math.max(1, Number(row.token))));
legacy[key] = { time: stage.time, activate: passTier(stage) > 0, progressLevel: level, progress,
free: (stage.free || []).slice(0, 30), passCheck: (stage.passCheck || []).slice(0, 30) };
}
return legacy;
}
export function passBounds(stage: any, count: number) {
const tier = passTier(stage);
const generated = Math.min(count, tier === 30 ? Math.max(40, (stage.unlocked || 0) + 5) : 40);
// 正常滚动至可解锁范围的下一档,后续奖励供上拉预览。
const content = Math.min(generated, tier === 30 ? Math.max(36, (stage.unlocked || 0) + 1) : 36);
return { generated, content };
}
export function canClaimPass(stage: any, index: number, left: boolean): boolean {
return !!stage && index >= 0 && index < stage.unlocked && (stage[left ? 'free' : 'passCheck'] || [])[index] === 1
&& (index < 35 ? left || passTier(stage) >= 18 : passTier(stage) === 30);
}
export function hasPassReward(stage: any): boolean {
return !!stage && (stage.free || []).some((_, i) => canClaimPass(stage, i, true) || canClaimPass(stage, i, false));
}