import { PASS_PERIOD } from './BattlePassModel'; /** Memory-only login cache; scene destruction must not reset a completed check. */ export default class BattlePassSettlementCheck { private static player: any; private static identity = ''; private static current: BattlePassSettlementCheck; static forPlayer(player: any): BattlePassSettlementCheck { const identity = String(player.uid || '') + ':' + String(player.token || ''); if (this.player !== player || this.identity !== identity || !this.current) { this.player = player; this.identity = identity; this.current = new BattlePassSettlementCheck(); } return this.current; } static resetLogin() { this.current = null; } private periodEnd = 0; private checkedAt = 0; private checkedVersion = -1; private offset = 0; version = 0; readonly reviewOrders = new Set(); hasSchedule(): boolean { return this.periodEnd > 0; } observeStart(start: number) { if (Number.isFinite(start) && start > 0) this.periodEnd = start + PASS_PERIOD; } syncClock(serverTime: number) { if (Number.isFinite(serverTime) && serverTime > 0) this.offset = serverTime - Date.now(); } invalidate() { this.version++; } needsCheck(orders: any[] = []): boolean { if (orders.some(order => /^battlepass(?:_30|_12)?$/.test(order.itemid) && !this.reviewOrders.has(order.outTradeNo))) return true; if (!this.checkedAt || this.checkedVersion !== this.version) return true; if (!this.periodEnd) return false; // Derive the first boundary after the last read, even if another API has // already advanced the global season while we were in GameScene. const nextEnd = this.periodEnd + (Math.floor((this.checkedAt - this.periodEnd) / PASS_PERIOD) + 1) * PASS_PERIOD; return Date.now() + this.offset >= nextEnd; } complete(serverTime: number, version: number) { this.checkedAt = serverTime; this.checkedVersion = version; } }