import { TableNames } from '../game/ConfigTableData'; import { ItemData } from '../game/ConfigProjectData'; import { OtherDataType } from '../game/GameDataEnums'; import { RankDataManager } from './RankDataManager'; type RankSnapDoc = { date: string; rank: number }; type SalaryDoc = { date: string; claimRank: number; claimed: boolean }; /** * 省榜每日俸禄(Daily_Rank 表): * - 领取奖励按「昨日」省榜名次(DailyRankProvinceSnap 快照) * - 每日仅可领取一次 */ export class DailyRankDataManager { static async ensureTable(): Promise { await gg.data.table.ensureTableLoaded(TableNames.Daily_Rank); } static getAllList(): ITableDaily_Rank[] { return gg.data.table.getTableList(TableNames.Daily_Rank) ?? []; } /** 拉取省榜后写入当日快照:YYYYMMDD|rank */ static recordProvinceBoardRank(rank: number): void { this.ensureToday(); const today = this._todayKey(); const r = Math.max(0, Math.floor(Number(rank) || 0)); gg.data.setStringData(OtherDataType.DailyRankProvinceSnap, `${today}|${r}`); gg.data.saveLocal(); } /** 今日可领俸禄对应的省榜名次(= 昨日快照名次,0=不可领) */ static getClaimBoardRank(): number { this.ensureToday(); const doc = this._parseSalaryDoc(); return doc.date === this._todayKey() ? doc.claimRank : 0; } /** 读档 / 跨天后刷新:用昨日快照生成本日可领名次 */ static ensureToday(): void { const today = this._todayKey(); const salary = this._parseSalaryDoc(); if (salary.date === today) return; const yesterday = this._yesterdayKey(today); const snap = this._parseSnapDoc(); const claimRank = (snap.date === yesterday && snap.rank > 0) ? snap.rank : 0; this._saveSalaryDoc(today, claimRank, false); } /** 配表最后一档 rank[1]:超出则不显示领取 */ static getMaxRewardBoardRank(): number { const list = this.getAllList(); if (list.length <= 0) return 0; const last = list[list.length - 1]; const range = last?.rank; if (!range || range.length < 2) return 0; return Math.max(0, Math.floor(Number(range[1]) || 0)); } /** 省榜名次是否在 Daily_Rank 任一区间内 */ static isBoardRankInRewardRange(boardRank: number): boolean { const r = Math.floor(Number(boardRank) || 0); if (r <= 0) return false; const max = this.getMaxRewardBoardRank(); if (max <= 0) return false; return r <= max; } /** 按省榜名次匹配 Daily_Rank 行 */ static findRowByBoardRank(boardRank: number): ITableDaily_Rank | null { const r = Math.floor(Number(boardRank) || 0); if (r <= 0) return null; for (const row of this.getAllList()) { const range = row?.rank; if (!range || range.length < 2) continue; const min = Math.floor(Number(range[0]) || 0); const max = Math.floor(Number(range[1]) || 0); if (r >= min && r <= max) return row; } return null; } static isClaimedToday(): boolean { this.ensureToday(); const doc = this._parseSalaryDoc(); return doc.date === this._todayKey() && doc.claimed; } static canClaimDailySalary(boardRank?: number): boolean { this.ensureToday(); const rank = boardRank === undefined ? this.getClaimBoardRank() : boardRank; if (!this.isBoardRankInRewardRange(rank)) return false; if (this.isClaimedToday()) return false; const row = this.findRowByBoardRank(rank); return this.parseDailyRankRewards(row).length > 0; } /** Daily_Rank.reward_rank → ItemData(与签到一致:2=成品 equip) */ static parseDailyRankRewards(row: ITableDaily_Rank | null | undefined): ItemData[] { const list: ItemData[] = []; for (const g of RankDataManager.parseRewardRank(row?.reward_rank)) { if (g.type === 2) { if (!gg.equip.getEquip(g.id)) continue; const item = new ItemData(g.id, g.num); item.isEquip = true; list.push(item); } else { const conf = gg.data.table.getItemData(g.id); if (!conf) continue; list.push(new ItemData(g.id, g.num, conf)); } } return list; } /** 领取今日省榜俸禄(按昨日名次);成功返回奖励列表 */ static claimDailySalary(boardRank?: number): ItemData[] | null { this.ensureToday(); const rank = boardRank === undefined ? this.getClaimBoardRank() : boardRank; if (!this.canClaimDailySalary(rank)) return null; const row = this.findRowByBoardRank(rank); const items = this.parseDailyRankRewards(row); if (items.length <= 0) return null; this._saveSalaryDoc(this._todayKey(), rank, true); gg.data.saveLocal(); return items; } static onCrossDay(): void { this.ensureToday(); } private static _parseSnapDoc(): RankSnapDoc { const raw = String(gg.data.getStringData(OtherDataType.DailyRankProvinceSnap) ?? '').trim(); if (!raw) return { date: '', rank: 0 }; const sep = raw.indexOf('|'); if (sep < 0) return { date: '', rank: 0 }; return { date: raw.slice(0, sep), rank: Math.max(0, Math.floor(Number(raw.slice(sep + 1)) || 0)), }; } private static _parseSalaryDoc(): SalaryDoc { const raw = String(gg.data.getStringData(OtherDataType.DailyRankSalary) ?? '').trim(); if (!raw) return { date: '', claimRank: 0, claimed: false }; const parts = raw.split('|'); if (parts.length >= 3) { return { date: parts[0], claimRank: Math.max(0, Math.floor(Number(parts[1]) || 0)), claimed: parts[2] === '1', }; } // 旧档兼容:仅 0/1,视为需按新规则重建 return { date: '', claimRank: 0, claimed: false }; } private static _saveSalaryDoc(date: string, claimRank: number, claimed: boolean): void { const rank = Math.max(0, Math.floor(Number(claimRank) || 0)); gg.data.setStringData( OtherDataType.DailyRankSalary, `${date}|${rank}|${claimed ? 1 : 0}`, ); } private static _todayKey(): string { const d = new Date(gg.data.getCurentTime2()); const y = d.getFullYear(); const m = `${d.getMonth() + 1}`.padStart(2, '0'); const day = `${d.getDate()}`.padStart(2, '0'); return `${y}${m}${day}`; } private static _yesterdayKey(fromToday: string): string { const y = Number(fromToday.slice(0, 4)) || 0; const m = Number(fromToday.slice(4, 6)) || 1; const d = Number(fromToday.slice(6, 8)) || 1; const dt = new Date(y, m - 1, d); dt.setDate(dt.getDate() - 1); const yy = dt.getFullYear(); const mm = `${dt.getMonth() + 1}`.padStart(2, '0'); const dd = `${dt.getDate()}`.padStart(2, '0'); return `${yy}${mm}${dd}`; } }