You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
7.2 KiB
190 lines
7.2 KiB
|
3 weeks ago
|
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<void> {
|
||
|
|
await gg.data.table.ensureTableLoaded(TableNames.Daily_Rank);
|
||
|
|
}
|
||
|
|
|
||
|
|
static getAllList(): ITableDaily_Rank[] {
|
||
|
|
return gg.data.table.getTableList<ITableDaily_Rank>(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}`;
|
||
|
|
}
|
||
|
|
}
|